[gnome-builder] libide-foundry: add IdeRunCommand
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide-foundry: add IdeRunCommand
- Date: Tue, 12 Jul 2022 06:39:11 +0000 (UTC)
commit 31635d1219d7ade52fbdb7a1537daab048f731d7
Author: Christian Hergert <chergert redhat com>
Date: Mon Jul 11 20:54:00 2022 -0700
libide-foundry: add IdeRunCommand
This removes IdeTestProvider and merges it into IdeRunCommandProvider. The
IdeTestManager now manages a wrapper object around the run command which is
stateful to track run state of the test.
src/libide/foundry/ide-run-command-provider.c | 190 +++
src/libide/foundry/ide-run-command-provider.h | 67 +
src/libide/foundry/ide-run-command.c | 543 ++++++++
src/libide/foundry/ide-run-command.h | 104 ++
src/libide/foundry/ide-run-commands.c | 415 ++++++
src/libide/foundry/ide-run-commands.h | 47 +
src/libide/foundry/ide-run-manager-private.h | 15 +-
src/libide/foundry/ide-run-manager.c | 1774 ++++++++++++++-----------
src/libide/foundry/ide-run-manager.h | 64 +-
src/libide/foundry/ide-runtime.c | 371 ++----
src/libide/foundry/ide-runtime.h | 66 +-
src/libide/foundry/ide-test-manager.c | 1062 ++++-----------
src/libide/foundry/ide-test-manager.h | 40 +-
src/libide/foundry/ide-test-private.h | 43 -
src/libide/foundry/ide-test-provider.c | 344 -----
src/libide/foundry/ide-test-provider.h | 86 --
src/libide/foundry/ide-test.c | 457 +++----
src/libide/foundry/ide-test.h | 63 +-
src/libide/foundry/libide-foundry.h | 5 +-
src/libide/foundry/meson.build | 2 -
20 files changed, 3023 insertions(+), 2735 deletions(-)
---
diff --git a/src/libide/foundry/ide-run-command-provider.c b/src/libide/foundry/ide-run-command-provider.c
new file mode 100644
index 000000000..72bff4af2
--- /dev/null
+++ b/src/libide/foundry/ide-run-command-provider.c
@@ -0,0 +1,190 @@
+/* ide-run-command-provider.c
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-run-command-provider"
+
+#include "config.h"
+
+#include "ide-build-manager.h"
+#include "ide-pipeline.h"
+#include "ide-run-command-provider.h"
+
+G_DEFINE_INTERFACE (IdeRunCommandProvider, ide_run_command_provider, IDE_TYPE_OBJECT)
+
+enum {
+ INVALIDATED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+static void
+ide_run_command_provider_default_init (IdeRunCommandProviderInterface *iface)
+{
+ signals[INVALIDATED] =
+ g_signal_new ("invalidated",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdeRunCommandProviderInterface, invalidated),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+}
+
+void
+ide_run_command_provider_list_commands_async (IdeRunCommandProvider *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_RUN_COMMAND_PROVIDER (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_RUN_COMMAND_PROVIDER_GET_IFACE (self)->list_commands_async (self, cancellable, callback, user_data);
+}
+
+/**
+ * ide_run_command_provider_list_commands_finish:
+ * @self: a #IdeRunCommandProvider
+ * @result: a #GAsyncResult
+ * @error: location for a #GError
+ *
+ * Completes request to list run commands.
+ *
+ * Returns: (transfer full): a #GListModel of #IdeRunCommand
+ */
+GListModel *
+ide_run_command_provider_list_commands_finish (IdeRunCommandProvider *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND_PROVIDER (self), NULL);
+
+ return IDE_RUN_COMMAND_PROVIDER_GET_IFACE (self)->list_commands_finish (self, result, error);
+}
+
+/**
+ * ide_run_command_provider_invalidate:
+ * @self: a #IdeRunCommandProvider
+ *
+ * Emits the #IdeRunCommandProvider::invalidated signal.
+ *
+ * This often results in #IdeRunCommands requesting a new set of results for
+ * the run command provider via ide_run_command_provider_list_commands_async().
+ */
+void
+ide_run_command_provider_invalidate (IdeRunCommandProvider *self)
+{
+ g_return_if_fail (IDE_IS_RUN_COMMAND_PROVIDER (self));
+
+ g_signal_emit (self, signals[INVALIDATED], 0);
+}
+
+static inline gulong
+get_signal_handler (IdeRunCommandProvider *self)
+{
+ return GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (self), "INVALIDATES_AT_PHASE"));
+}
+
+static inline void
+set_signal_handler (IdeRunCommandProvider *self,
+ gulong handler_id)
+{
+ g_object_set_data (G_OBJECT (self), "INVALIDATES_AT_PHASE", GSIZE_TO_POINTER (handler_id));
+}
+
+static void
+ide_run_command_provider_pipeline_notify_phase_cb (IdeRunCommandProvider *self,
+ GParamSpec *pspec,
+ IdePipeline *pipeline)
+{
+ IdePipelinePhase current_phase;
+ IdePipelinePhase invalidate_phase;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (self));
+ g_assert (IDE_IS_PIPELINE (pipeline));
+
+ invalidate_phase = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (self), "PIPELINE_PHASE"));
+ current_phase = ide_pipeline_get_phase (pipeline);
+
+ /* Only invalidate when the phase exactly matches. We could check to see if
+ * the current phase is > than the last notified phase, but generally
+ * speaking, the users of this have a pipeline stage attached at exactly that
+ * phase to be notified of.
+ */
+ if (invalidate_phase != 0 && current_phase != 0 && invalidate_phase == current_phase)
+ ide_run_command_provider_invalidate (self);
+
+ IDE_EXIT;
+}
+
+/**
+ * ide_run_command_provider_invalidates_at_phase:
+ * @self: an #IdeRunCommandProvider
+ * @phase: an #IdePipelinePhase
+ *
+ * Invalidates the provider when @phase is reached.
+ *
+ * This is a helper for run command provider implementations to use which
+ * will automatically invalidate @self when pipeline @phase is reached.
+ *
+ * Calling this function will unset any previous call to the function. Setting
+ * @phase to 0 will not subscribe to any new phase.
+ */
+void
+ide_run_command_provider_invalidates_at_phase (IdeRunCommandProvider *self,
+ IdePipelinePhase phase)
+{
+ GSignalGroup *signal_group;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_RUN_COMMAND_PROVIDER (self));
+
+ g_object_set_data (G_OBJECT (self), "PIPELINE_PHASE", GUINT_TO_POINTER (phase));
+
+ if (phase == 0)
+ IDE_EXIT;
+
+
+ if (!(signal_group = g_object_get_data (G_OBJECT (self), "PIPELINE_SIGNAL_GROUP")))
+ {
+ IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
+ IdeBuildManager *build_manager = ide_build_manager_from_context (context);
+
+ signal_group = g_signal_group_new (IDE_TYPE_PIPELINE);
+ g_signal_group_connect_object (signal_group,
+ "notify::phase",
+ G_CALLBACK (ide_run_command_provider_pipeline_notify_phase_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_object_set_data_full (G_OBJECT (self),
+ "PIPELINE_SIGNAL_GROUP",
+ signal_group,
+ g_object_unref);
+ g_object_bind_property (build_manager, "pipeline", signal_group, "target", G_BINDING_SYNC_CREATE);
+ }
+
+ IDE_EXIT;
+}
diff --git a/src/libide/foundry/ide-run-command-provider.h b/src/libide/foundry/ide-run-command-provider.h
new file mode 100644
index 000000000..c4617dbc7
--- /dev/null
+++ b/src/libide/foundry/ide-run-command-provider.h
@@ -0,0 +1,67 @@
+/* ide-run-command-provider.h
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-pipeline-phase.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_RUN_COMMAND_PROVIDER (ide_run_command_provider_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_INTERFACE (IdeRunCommandProvider, ide_run_command_provider, IDE, RUN_COMMAND_PROVIDER, IdeObject)
+
+struct _IdeRunCommandProviderInterface
+{
+ GTypeInterface parent_iface;
+
+ void (*invalidated) (IdeRunCommandProvider *self);
+ void (*list_commands_async) (IdeRunCommandProvider *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GListModel *(*list_commands_finish) (IdeRunCommandProvider *self,
+ GAsyncResult *result,
+ GError **error);
+};
+
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_provider_invalidate (IdeRunCommandProvider *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_provider_invalidates_at_phase (IdeRunCommandProvider *self,
+ IdePipelinePhase phase);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_provider_list_commands_async (IdeRunCommandProvider *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+GListModel *ide_run_command_provider_list_commands_finish (IdeRunCommandProvider *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/libide/foundry/ide-run-command.c b/src/libide/foundry/ide-run-command.c
new file mode 100644
index 000000000..f9dc6a8b1
--- /dev/null
+++ b/src/libide/foundry/ide-run-command.c
@@ -0,0 +1,543 @@
+/* ide-run-command.c
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-run-command"
+
+#include "config.h"
+
+#include "ide-build-manager.h"
+#include "ide-foundry-enums.h"
+#include "ide-pipeline.h"
+#include "ide-run-command.h"
+#include "ide-run-context.h"
+
+typedef struct
+{
+ char *id;
+ char *cwd;
+ char *display_name;
+ char **environ;
+ char **argv;
+ char **languages;
+ int priority;
+ IdeRunCommandKind kind;
+} IdeRunCommandPrivate;
+
+enum {
+ PROP_0,
+ PROP_ARGV,
+ PROP_CWD,
+ PROP_DISPLAY_NAME,
+ PROP_ENVIRON,
+ PROP_ID,
+ PROP_KIND,
+ PROP_LANGUAGES,
+ PROP_PRIORITY,
+ N_PROPS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeRunCommand, ide_run_command, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_run_command_real_prepare_to_run (IdeRunCommand *self,
+ IdeRunContext *run_context,
+ IdeContext *context)
+{
+ g_autoptr(GFile) workdir = NULL;
+ IdeBuildManager *build_manager = NULL;
+ g_auto(GStrv) environ = NULL;
+ IdePipeline *pipeline = NULL;
+ const char * const *argv;
+ const char * const *env;
+ const char *builddir;
+ const char *srcdir;
+ const char *cwd;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_RUN_COMMAND (self));
+ g_assert (IDE_IS_RUN_CONTEXT (run_context));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ workdir = ide_context_ref_workdir (context);
+ srcdir = g_file_peek_path (workdir);
+ builddir = g_file_peek_path (workdir);
+
+ if (ide_context_has_project (context))
+ {
+ build_manager = ide_build_manager_from_context (context);
+ pipeline = ide_build_manager_get_pipeline (build_manager);
+ builddir = ide_pipeline_get_builddir (pipeline);
+ srcdir = ide_pipeline_get_srcdir (pipeline);
+ }
+
+ environ = g_environ_setenv (environ, "BUILDDIR", builddir, TRUE);
+ environ = g_environ_setenv (environ, "SRCDIR", srcdir, TRUE);
+ environ = g_environ_setenv (environ, "USER", g_get_user_name (), TRUE);
+ environ = g_environ_setenv (environ, "HOME", g_get_home_dir (), TRUE);
+
+ ide_run_context_push_expansion (run_context, (const char * const *)environ);
+
+ if ((cwd = ide_run_command_get_cwd (IDE_RUN_COMMAND (self))))
+ ide_run_context_set_cwd (run_context, cwd);
+
+ if ((argv = ide_run_command_get_argv (IDE_RUN_COMMAND (self))))
+ ide_run_context_append_args (run_context, argv);
+
+ if ((env = ide_run_command_get_environ (IDE_RUN_COMMAND (self))))
+ ide_run_context_add_environ (run_context, env);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_command_finalize (GObject *object)
+{
+ IdeRunCommand *self = (IdeRunCommand *)object;
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_clear_pointer (&priv->id, g_free);
+ g_clear_pointer (&priv->cwd, g_free);
+ g_clear_pointer (&priv->display_name, g_free);
+ g_clear_pointer (&priv->environ, g_strfreev);
+ g_clear_pointer (&priv->argv, g_strfreev);
+ g_clear_pointer (&priv->languages, g_strfreev);
+
+ G_OBJECT_CLASS (ide_run_command_parent_class)->finalize (object);
+}
+
+static void
+ide_run_command_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeRunCommand *self = IDE_RUN_COMMAND (object);
+
+ switch (prop_id)
+ {
+ case PROP_CWD:
+ g_value_set_string (value, ide_run_command_get_cwd (self));
+ break;
+
+ case PROP_DISPLAY_NAME:
+ g_value_set_string (value, ide_run_command_get_display_name (self));
+ break;
+
+ case PROP_ARGV:
+ g_value_set_boxed (value, ide_run_command_get_argv (self));
+ break;
+
+ case PROP_ENVIRON:
+ g_value_set_boxed (value, ide_run_command_get_environ (self));
+ break;
+
+ case PROP_ID:
+ g_value_set_string (value, ide_run_command_get_id (self));
+ break;
+
+ case PROP_KIND:
+ g_value_set_enum (value, ide_run_command_get_kind (self));
+ break;
+
+ case PROP_LANGUAGES:
+ g_value_set_boxed (value, ide_run_command_get_languages (self));
+ break;
+
+ case PROP_PRIORITY:
+ g_value_set_int (value, ide_run_command_get_priority (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_run_command_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeRunCommand *self = IDE_RUN_COMMAND (object);
+
+ switch (prop_id)
+ {
+ case PROP_CWD:
+ ide_run_command_set_cwd (self, g_value_get_string (value));
+ break;
+
+ case PROP_DISPLAY_NAME:
+ ide_run_command_set_display_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_ARGV:
+ ide_run_command_set_argv (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_ENVIRON:
+ ide_run_command_set_environ (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_ID:
+ ide_run_command_set_id (self, g_value_get_string (value));
+ break;
+
+ case PROP_KIND:
+ ide_run_command_set_kind (self, g_value_get_enum (value));
+ break;
+
+ case PROP_LANGUAGES:
+ ide_run_command_set_languages (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_PRIORITY:
+ ide_run_command_set_priority (self, g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_run_command_class_init (IdeRunCommandClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_run_command_finalize;
+ object_class->get_property = ide_run_command_get_property;
+ object_class->set_property = ide_run_command_set_property;
+
+ klass->prepare_to_run = ide_run_command_real_prepare_to_run;
+
+ properties [PROP_ARGV] =
+ g_param_spec_boxed ("argv", NULL, NULL,
+ G_TYPE_STRV,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CWD] =
+ g_param_spec_string ("cwd", NULL, NULL,
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DISPLAY_NAME] =
+ g_param_spec_string ("display-name", NULL, NULL,
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ENVIRON] =
+ g_param_spec_boxed ("environ", NULL, NULL,
+ G_TYPE_STRV,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ID] =
+ g_param_spec_string ("id", NULL, NULL,
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_KIND] =
+ g_param_spec_enum ("kind", NULL, NULL,
+ IDE_TYPE_RUN_COMMAND_KIND,
+ IDE_RUN_COMMAND_KIND_UNKNOWN,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * IdeRunCommand:languages:
+ *
+ * Contains the programming languages used.
+ *
+ * This is to be set by run command providers when they know what languages
+ * are used to create the program spawned by the run command. This can be
+ * used by debuggers to ensure that a suitable debugger is chosen for a given
+ * language used.
+ */
+ properties [PROP_LANGUAGES] =
+ g_param_spec_boxed ("languages", NULL, NULL,
+ G_TYPE_STRV,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PRIORITY] =
+ g_param_spec_int ("priority", NULL, NULL,
+ G_MININT, G_MAXINT, 0,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_run_command_init (IdeRunCommand *self)
+{
+}
+
+IdeRunCommand *
+ide_run_command_new (void)
+{
+ return g_object_new (IDE_TYPE_RUN_COMMAND, NULL);
+}
+
+const char *
+ide_run_command_get_id (IdeRunCommand *self)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self), NULL);
+
+ return priv->id;
+}
+
+void
+ide_run_command_set_id (IdeRunCommand *self,
+ const char *id)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+
+ if (g_strcmp0 (priv->id, id) != 0)
+ {
+ g_free (priv->id);
+ priv->id = g_strdup (id);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]);
+ }
+}
+
+const char *
+ide_run_command_get_cwd (IdeRunCommand *self)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self), NULL);
+
+ return priv->cwd;
+}
+
+void
+ide_run_command_set_cwd (IdeRunCommand *self,
+ const char *cwd)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+
+ if (g_strcmp0 (priv->cwd, cwd) != 0)
+ {
+ g_free (priv->cwd);
+ priv->cwd = g_strdup (cwd);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CWD]);
+ }
+}
+
+const char *
+ide_run_command_get_display_name (IdeRunCommand *self)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self), NULL);
+
+ return priv->display_name;
+}
+
+void
+ide_run_command_set_display_name (IdeRunCommand *self,
+ const char *display_name)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+
+ if (g_strcmp0 (priv->display_name, display_name) != 0)
+ {
+ g_free (priv->display_name);
+ priv->display_name = g_strdup (display_name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISPLAY_NAME]);
+ }
+}
+
+const char * const *
+ide_run_command_get_argv (IdeRunCommand *self)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self), NULL);
+
+ return (const char * const *)priv->argv;
+}
+
+void
+ide_run_command_set_argv (IdeRunCommand *self,
+ const char * const *argv)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+
+ if (argv == (const char * const *)priv->argv)
+ return;
+
+ g_strfreev (priv->argv);
+ priv->argv = g_strdupv ((char **)argv);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ARGV]);
+}
+
+const char * const *
+ide_run_command_get_environ (IdeRunCommand *self)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self), NULL);
+
+ return (const char * const *)priv->environ;
+}
+
+void
+ide_run_command_set_environ (IdeRunCommand *self,
+ const char * const *environ)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+
+ if (environ == (const char * const *)priv->environ)
+ return;
+
+ g_strfreev (priv->environ);
+ priv->environ = g_strdupv ((char **)environ);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENVIRON]);
+}
+
+int
+ide_run_command_get_priority (IdeRunCommand *self)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self), -1);
+
+ return priv->priority;
+}
+
+void
+ide_run_command_set_priority (IdeRunCommand *self,
+ int priority)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+
+ if (priority != priv->priority)
+ {
+ priv->priority = priority;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PRIORITY]);
+ }
+}
+
+IdeRunCommandKind
+ide_run_command_get_kind (IdeRunCommand *self)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self), 0);
+
+ return priv->kind;
+}
+
+/**
+ * ide_run_command_set_kind:
+ * @self: a #IdeRunCommand
+ *
+ * Sets the kind of command.
+ *
+ * This is useful for #IdeRunCommandProvider that want to specify
+ * the type of command that is being provided. Doing so allows tooling
+ * in Builder to treat that information specially, such as showing tags
+ * next to the row in UI or including it in "Unit Test" browsers.
+ */
+void
+ide_run_command_set_kind (IdeRunCommand *self,
+ IdeRunCommandKind kind)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+ g_return_if_fail (kind <= IDE_RUN_COMMAND_KIND_USER_DEFINED);
+
+ if (priv->kind != kind)
+ {
+ priv->kind = kind;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_KIND]);
+ }
+}
+
+const char * const *
+ide_run_command_get_languages (IdeRunCommand *self)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self), NULL);
+
+ return (const char * const *)priv->languages;
+}
+
+void
+ide_run_command_set_languages (IdeRunCommand *self,
+ const char * const *languages)
+{
+ IdeRunCommandPrivate *priv = ide_run_command_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+
+ if (languages == (const char * const *)priv->languages ||
+ (languages != NULL &&
+ priv->languages != NULL &&
+ g_strv_equal ((const char * const *)priv->languages, languages)))
+ return;
+
+ g_strfreev (priv->languages);
+ priv->languages = g_strdupv ((char **)languages);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LANGUAGES]);
+}
+
+/**
+ * ide_run_command_prepare_to_run:
+ * @self: a #IdeRunCommand
+ * @run_context: an #IdeRunContext
+ * @context: an #IdeContext
+ *
+ * Prepares the run command to be run within @run_context.
+ *
+ * This requires that the run command add anything necessary to the
+ * @run_context so that the command can be run.
+ *
+ * Subclasses may override this to implement custom functionality such as
+ * locality-based execution (see shellcmd plugin).
+ */
+void
+ide_run_command_prepare_to_run (IdeRunCommand *self,
+ IdeRunContext *run_context,
+ IdeContext *context)
+{
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self));
+ g_return_if_fail (IDE_IS_RUN_CONTEXT (run_context));
+ g_return_if_fail (IDE_IS_CONTEXT (context));
+
+ IDE_RUN_COMMAND_GET_CLASS (self)->prepare_to_run (self, run_context, context);
+}
diff --git a/src/libide/foundry/ide-run-command.h b/src/libide/foundry/ide-run-command.h
new file mode 100644
index 000000000..a516dc41f
--- /dev/null
+++ b/src/libide/foundry/ide-run-command.h
@@ -0,0 +1,104 @@
+/* ide-run-command.h
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-foundry-types.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_RUN_COMMAND (ide_run_command_get_type())
+
+typedef enum
+{
+ IDE_RUN_COMMAND_KIND_UNKNOWN = 0,
+ IDE_RUN_COMMAND_KIND_APPLICATION,
+ IDE_RUN_COMMAND_KIND_UTILITY,
+ IDE_RUN_COMMAND_KIND_TEST,
+ IDE_RUN_COMMAND_KIND_BENCHMARK,
+ IDE_RUN_COMMAND_KIND_USER_DEFINED,
+} IdeRunCommandKind;
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (IdeRunCommand, ide_run_command, IDE, RUN_COMMAND, GObject)
+
+struct _IdeRunCommandClass
+{
+ GObjectClass parent_class;
+
+ void (*prepare_to_run) (IdeRunCommand *self,
+ IdeRunContext *run_context,
+ IdeContext *context);
+};
+
+IDE_AVAILABLE_IN_ALL
+IdeRunCommand *ide_run_command_new (void);
+IDE_AVAILABLE_IN_ALL
+const char *ide_run_command_get_id (IdeRunCommand *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_set_id (IdeRunCommand *self,
+ const char *id);
+IDE_AVAILABLE_IN_ALL
+const char *ide_run_command_get_cwd (IdeRunCommand *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_set_cwd (IdeRunCommand *self,
+ const char *cwd);
+IDE_AVAILABLE_IN_ALL
+const char *ide_run_command_get_display_name (IdeRunCommand *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_set_display_name (IdeRunCommand *self,
+ const char *display_name);
+IDE_AVAILABLE_IN_ALL
+const char * const *ide_run_command_get_argv (IdeRunCommand *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_set_argv (IdeRunCommand *self,
+ const char * const *argv);
+IDE_AVAILABLE_IN_ALL
+const char * const *ide_run_command_get_environ (IdeRunCommand *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_set_environ (IdeRunCommand *self,
+ const char * const *environ);
+IDE_AVAILABLE_IN_ALL
+int ide_run_command_get_priority (IdeRunCommand *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_set_priority (IdeRunCommand *self,
+ int priority);
+IDE_AVAILABLE_IN_ALL
+IdeRunCommandKind ide_run_command_get_kind (IdeRunCommand *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_set_kind (IdeRunCommand *self,
+ IdeRunCommandKind kind);
+IDE_AVAILABLE_IN_ALL
+const char * const *ide_run_command_get_languages (IdeRunCommand *self);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_set_languages (IdeRunCommand *self,
+ const char * const *languages);
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_prepare_to_run (IdeRunCommand *self,
+ IdeRunContext *run_context,
+ IdeContext *context);
+
+G_END_DECLS
diff --git a/src/libide/foundry/ide-run-commands.c b/src/libide/foundry/ide-run-commands.c
new file mode 100644
index 000000000..fd3056f41
--- /dev/null
+++ b/src/libide/foundry/ide-run-commands.c
@@ -0,0 +1,415 @@
+/* ide-run-commands.c
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-run-commands"
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libide-plugins.h>
+#include <libide-threading.h>
+
+#include "ide-run-command.h"
+#include "ide-run-command-provider.h"
+#include "ide-run-commands.h"
+
+#define RELOAD_TIMEOUT_MSEC 250
+
+struct _IdeRunCommands
+{
+ IdeObject parent_instance;
+ IdeExtensionSetAdapter *addins;
+ GListStore *models;
+ GtkFlattenListModel *flatten_model;
+ GHashTable *provider_to_model;
+ GQueue invalid;
+ guint reload_source;
+};
+
+static GType
+ide_run_commands_get_item_type (GListModel *model)
+{
+ return IDE_TYPE_RUN_COMMAND;
+}
+
+static guint
+ide_run_commands_get_n_items (GListModel *model)
+{
+ IdeRunCommands *self = IDE_RUN_COMMANDS (model);
+ return g_list_model_get_n_items (G_LIST_MODEL (self->flatten_model));
+}
+
+static gpointer
+ide_run_commands_get_item (GListModel *model,
+ guint position)
+{
+ IdeRunCommands *self = IDE_RUN_COMMANDS (model);
+ return g_list_model_get_item (G_LIST_MODEL (self->flatten_model), position);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item_type = ide_run_commands_get_item_type;
+ iface->get_n_items = ide_run_commands_get_n_items;
+ iface->get_item = ide_run_commands_get_item;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (IdeRunCommands, ide_run_commands, IDE_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static void
+ide_run_commands_items_changed_cb (IdeRunCommands *self,
+ guint position,
+ guint removed,
+ guint added,
+ GListModel *model)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+ g_assert (G_IS_LIST_MODEL (model));
+
+ g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_list_commands_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRunCommandProvider *provider = (IdeRunCommandProvider *)object;
+ g_autoptr(IdeRunCommands) self = user_data;
+ g_autoptr(GListModel) model = NULL;
+ g_autoptr(GError) error = NULL;
+ GListModel *old_model;
+ gboolean found;
+ guint position;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+
+ if (!(model = ide_run_command_provider_list_commands_finish (provider, result, &error)))
+ {
+ /* Just keep the old one around until things succeed */
+ g_debug ("Failed to list run commands from %s: %s",
+ G_OBJECT_TYPE_NAME (provider), error->message);
+ IDE_EXIT;
+ }
+
+ /* Do nothing if the model didn't change */
+ old_model = g_hash_table_lookup (self->provider_to_model, provider);
+ if (old_model == model)
+ IDE_EXIT;
+
+ g_assert (old_model != model);
+ g_assert (!old_model || G_IS_LIST_MODEL (old_model));
+ g_assert (G_IS_LIST_MODEL (model));
+
+ /* First try to locate our model */
+ if (old_model != NULL)
+ found = g_list_store_find (self->models, old_model, &position);
+ else
+ found = FALSE;
+
+ /* Now ensure our hashtable is up to date for re-entrancy purposes */
+ g_hash_table_insert (self->provider_to_model,
+ g_object_ref (provider),
+ g_object_ref (model));
+
+ if (found)
+ g_list_store_splice (self->models, position, 1, (gpointer *)&model, 1);
+ else
+ g_list_store_append (self->models, model);
+
+ IDE_EXIT;
+}
+
+static gboolean
+ide_run_commands_reload_source_func (gpointer data)
+{
+ g_autoptr(GCancellable) cancellable = NULL;
+ IdeRunCommandProvider *provider;
+ IdeRunCommands *self = data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+
+ self->reload_source = 0;
+
+ cancellable = ide_object_ref_cancellable (IDE_OBJECT (self));
+
+ while ((provider = g_queue_pop_head (&self->invalid)))
+ ide_run_command_provider_list_commands_async (provider,
+ cancellable,
+ ide_run_commands_list_commands_cb,
+ g_object_ref (self));
+
+ IDE_RETURN (G_SOURCE_REMOVE);
+}
+
+static void
+ide_run_commands_provider_invalidated_cb (IdeRunCommands *self,
+ IdeRunCommandProvider *provider)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
+
+ if (g_queue_find (&self->invalid, provider) == NULL)
+ {
+ g_queue_push_tail (&self->invalid, provider);
+
+ if (self->reload_source == 0)
+ self->reload_source = g_timeout_add (RELOAD_TIMEOUT_MSEC,
+ ide_run_commands_reload_source_func,
+ self);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_provider_added_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeRunCommandProvider *provider = (IdeRunCommandProvider *)exten;
+ IdeRunCommands *self = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+
+ g_signal_connect_object (provider,
+ "invalidated",
+ G_CALLBACK (ide_run_commands_provider_invalidated_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_run_commands_provider_invalidated_cb (self, provider);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_provider_removed_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeRunCommandProvider *provider = (IdeRunCommandProvider *)exten;
+ g_autoptr(IdeRunCommandProvider) stolen_key = NULL;
+ g_autoptr(GListModel) stolen_value = NULL;
+ IdeRunCommands *self = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+
+ if (g_hash_table_steal_extended (self->provider_to_model,
+ provider,
+ (gpointer *)&stolen_key,
+ (gpointer *)&stolen_value))
+ {
+ g_queue_remove (&self->invalid, provider);
+
+ if (self->invalid.length == 0)
+ g_clear_handle_id (&self->reload_source, g_source_remove);
+
+ if (stolen_value != NULL)
+ {
+ guint position;
+
+ if (g_list_store_find (self->models, stolen_value, &position))
+ g_list_store_remove (self->models, position);
+ }
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_parent_set (IdeObject *object,
+ IdeObject *parent)
+{
+ IdeRunCommands *self = (IdeRunCommands *)object;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+ g_assert (!parent || IDE_IS_OBJECT (parent));
+
+ if (parent == NULL)
+ IDE_EXIT;
+
+ self->addins = ide_extension_set_adapter_new (IDE_OBJECT (self),
+ peas_engine_get_default (),
+ IDE_TYPE_RUN_COMMAND_PROVIDER,
+ NULL, NULL);
+ g_signal_connect (self->addins,
+ "extension-added",
+ G_CALLBACK (ide_run_commands_provider_added_cb),
+ self);
+ g_signal_connect (self->addins,
+ "extension-removed",
+ G_CALLBACK (ide_run_commands_provider_removed_cb),
+ self);
+ ide_extension_set_adapter_foreach (self->addins,
+ ide_run_commands_provider_added_cb,
+ self);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_dispose (GObject *object)
+{
+ IdeRunCommands *self = (IdeRunCommands *)object;
+
+ g_queue_clear (&self->invalid);
+ g_clear_handle_id (&self->reload_source, g_source_remove);
+ ide_clear_and_destroy_object (&self->addins);
+ g_clear_object (&self->models);
+ g_clear_object (&self->flatten_model);
+ g_clear_pointer (&self->provider_to_model, g_hash_table_unref);
+
+ G_OBJECT_CLASS (ide_run_commands_parent_class)->dispose (object);
+}
+
+static void
+ide_run_commands_class_init (IdeRunCommandsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ IdeObjectClass *ide_object_class = IDE_OBJECT_CLASS (klass);
+
+ object_class->dispose = ide_run_commands_dispose;
+
+ ide_object_class->parent_set = ide_run_commands_parent_set;
+}
+
+static void
+ide_run_commands_init (IdeRunCommands *self)
+{
+ self->provider_to_model = g_hash_table_new_full (NULL, NULL, g_object_unref, g_object_unref);
+ self->models = g_list_store_new (G_TYPE_LIST_MODEL);
+ self->flatten_model = gtk_flatten_list_model_new (g_object_ref (G_LIST_MODEL (self->models)));
+
+ g_signal_connect_object (self->flatten_model,
+ "items-changed",
+ G_CALLBACK (ide_run_commands_items_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+/**
+ * ide_run_commands_dup_by_id:
+ * @self: a #IdeRunCommands
+ * @id: (nullable): the id of the run command
+ *
+ * Finds an #IdeRunCommand by it's id.
+ *
+ * %NULL is allowed for @id out of convenience, but will return %NULL.
+ *
+ * Returns: (transfer full) (nullable): an #IdeRunCommand or %NULL
+ */
+IdeRunCommand *
+ide_run_commands_dup_by_id (IdeRunCommands *self,
+ const char *id)
+{
+ guint n_items;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMANDS (self), NULL);
+
+ if (id == NULL)
+ IDE_RETURN (NULL);
+
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
+
+ IDE_TRACE_MSG ("Locating command by id %s in list of %u commands", id, n_items);
+
+ for (guint i = 0; i < n_items; i++)
+ {
+ g_autoptr(IdeRunCommand) run_command = g_list_model_get_item (G_LIST_MODEL (self), i);
+ const char *run_command_id = ide_run_command_get_id (run_command);
+
+ if (ide_str_equal0 (run_command_id, id))
+ IDE_RETURN (g_steal_pointer (&run_command));
+ }
+
+ IDE_RETURN (NULL);
+}
+
+static gboolean
+filter_run_command_by_kind (gpointer item,
+ gpointer user_data)
+{
+ return ide_run_command_get_kind (item) == GPOINTER_TO_INT (user_data);
+}
+
+/**
+ * ide_run_commands_list_by_kind:
+ * @self: an #IdeRunCommands
+ * @kind: an #IdeRunCommandKind
+ *
+ * Creates a new #GListModel of #IdeRunCommand filtered by @kind
+ *
+ * The model will update as new commands are added or removed from @self.
+ *
+ * Returns: (transfer full): a #GListModel
+ */
+GListModel *
+ide_run_commands_list_by_kind (IdeRunCommands *self,
+ IdeRunCommandKind kind)
+{
+ GtkCustomFilter *filter = NULL;
+ GtkFilterListModel *model = NULL;
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMANDS (self), NULL);
+
+ filter = gtk_custom_filter_new (filter_run_command_by_kind, GINT_TO_POINTER (kind), NULL);
+ model = gtk_filter_list_model_new (g_object_ref (G_LIST_MODEL (self)), GTK_FILTER (filter));
+
+ return G_LIST_MODEL (model);
+}
diff --git a/src/libide/foundry/ide-run-commands.h b/src/libide/foundry/ide-run-commands.h
new file mode 100644
index 000000000..77ad0533b
--- /dev/null
+++ b/src/libide/foundry/ide-run-commands.h
@@ -0,0 +1,47 @@
+/* ide-run-commands.h
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-run-command.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_RUN_COMMANDS (ide_run_commands_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeRunCommands, ide_run_commands, IDE, RUN_COMMANDS, IdeObject)
+
+IDE_AVAILABLE_IN_ALL
+IdeRunCommands *ide_run_commands_from_context (IdeContext *context);
+IDE_AVAILABLE_IN_ALL
+GListModel *ide_run_commands_list_by_kind (IdeRunCommands *self,
+ IdeRunCommandKind kind);
+IDE_AVAILABLE_IN_ALL
+IdeRunCommand *ide_run_commands_dup_by_id (IdeRunCommands *self,
+ const char *id);
+
+G_END_DECLS
diff --git a/src/libide/foundry/ide-run-manager-private.h b/src/libide/foundry/ide-run-manager-private.h
index c841a8948..8b2e7c762 100644
--- a/src/libide/foundry/ide-run-manager-private.h
+++ b/src/libide/foundry/ide-run-manager-private.h
@@ -24,19 +24,6 @@
G_BEGIN_DECLS
-typedef struct
-{
- gchar *id;
- gchar *title;
- gchar *icon_name;
- gchar *accel;
- gint priority;
- IdeRunHandler handler;
- gpointer handler_data;
- GDestroyNotify handler_data_destroy;
-} IdeRunHandlerInfo;
-
-const GList *_ide_run_manager_get_handlers (IdeRunManager *self);
-void _ide_run_manager_drop_caches (IdeRunManager *self);
+void _ide_run_manager_drop_caches (IdeRunManager *self);
G_END_DECLS
diff --git a/src/libide/foundry/ide-run-manager.c b/src/libide/foundry/ide-run-manager.c
index c70b16170..a1a910705 100644
--- a/src/libide/foundry/ide-run-manager.c
+++ b/src/libide/foundry/ide-run-manager.c
@@ -23,24 +23,31 @@
#include "config.h"
#include <glib/gi18n.h>
-#include <libide-threading.h>
-#include <libide-vcs.h>
+#include <gtk/gtk.h>
+#include <unistd.h>
+
#include <libpeas/peas.h>
#include <libpeas/peas-autocleanups.h>
+#include <libide-core.h>
+#include <libide-plugins.h>
+#include <libide-threading.h>
+#include <libide-vcs.h>
+
#include "ide-private.h"
#include "ide-build-manager.h"
#include "ide-build-system.h"
-#include "ide-build-target-provider.h"
-#include "ide-build-target.h"
-#include "ide-config-manager.h"
-#include "ide-config.h"
+#include "ide-deploy-strategy.h"
#include "ide-device-manager.h"
#include "ide-foundry-compat.h"
+#include "ide-no-tool-private.h"
+#include "ide-run-command.h"
+#include "ide-run-command-provider.h"
+#include "ide-run-context.h"
#include "ide-run-manager-private.h"
#include "ide-run-manager.h"
-#include "ide-runner.h"
+#include "ide-run-tool-private.h"
#include "ide-runtime.h"
struct _IdeRunManager
@@ -48,11 +55,13 @@ struct _IdeRunManager
IdeObject parent_instance;
GCancellable *cancellable;
- IdeBuildTarget *build_target;
IdeNotification *notif;
+ IdeExtensionSetAdapter *run_command_providers;
+ IdeExtensionSetAdapter *run_tools;
+ IdeRunTool *run_tool;
- const IdeRunHandlerInfo *handler;
- GList *handlers;
+ IdeSubprocess *current_subprocess;
+ IdeRunCommand *current_run_command;
/* Keep track of last change sequence from the file monitor
* so that we can maybe skip past install phase and make
@@ -61,45 +70,59 @@ struct _IdeRunManager
guint64 last_change_seq;
guint64 pending_last_change_seq;
- guint busy : 1;
+ char *default_run_command;
+
+ guint busy;
+
+ guint messages_debug_all : 1;
+ guint has_installed_once : 1;
+ guint sent_signal : 1;
};
-typedef struct
-{
- GList *providers;
- GPtrArray *results;
- guint active;
-} DiscoverState;
-
-static void initable_iface_init (GInitableIface *iface);
-static void ide_run_manager_actions_run (IdeRunManager *self,
- GVariant *param);
-static void ide_run_manager_actions_run_with_handler (IdeRunManager *self,
- GVariant *param);
-static void ide_run_manager_actions_stop (IdeRunManager *self,
- GVariant *param);
-
-DZL_DEFINE_ACTION_GROUP (IdeRunManager, ide_run_manager, {
+static void initable_iface_init (GInitableIface *iface);
+static void ide_run_manager_actions_run (IdeRunManager *self,
+ GVariant *param);
+static void ide_run_manager_actions_run_with_handler (IdeRunManager *self,
+ GVariant *param);
+static void ide_run_manager_actions_stop (IdeRunManager *self,
+ GVariant *param);
+static void ide_run_manager_actions_messages_debug_all (IdeRunManager *self,
+ GVariant *param);
+static void ide_run_manager_actions_default_run_command (IdeRunManager *self,
+ GVariant *param);
+static void ide_run_manager_actions_color_scheme (IdeRunManager *self,
+ GVariant *param);
+static void ide_run_manager_actions_high_contrast (IdeRunManager *self,
+ GVariant *param);
+static void ide_run_manager_actions_text_direction (IdeRunManager *self,
+ GVariant *param);
+
+IDE_DEFINE_ACTION_GROUP (IdeRunManager, ide_run_manager, {
{ "run", ide_run_manager_actions_run },
{ "run-with-handler", ide_run_manager_actions_run_with_handler, "s" },
{ "stop", ide_run_manager_actions_stop },
+ { "messages-debug-all", ide_run_manager_actions_messages_debug_all, NULL, "false" },
+ { "default-run-command", ide_run_manager_actions_default_run_command, "s", "''" },
+ { "color-scheme", ide_run_manager_actions_color_scheme, "s", "'follow'" },
+ { "high-contrast", ide_run_manager_actions_high_contrast, NULL, "false" },
+ { "text-direction", ide_run_manager_actions_text_direction, "s", "''" },
})
G_DEFINE_TYPE_EXTENDED (IdeRunManager, ide_run_manager, IDE_TYPE_OBJECT, G_TYPE_FLAG_FINAL,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
- G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
- ide_run_manager_init_action_group))
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, ide_run_manager_init_action_group))
enum {
PROP_0,
PROP_BUSY,
- PROP_HANDLER,
- PROP_BUILD_TARGET,
+ PROP_ICON_NAME,
+ PROP_RUN_TOOL,
N_PROPS
};
enum {
RUN,
+ STARTED,
STOPPED,
N_SIGNALS
};
@@ -107,63 +130,132 @@ enum {
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
-static void
-discover_state_free (gpointer data)
+static IdeRunTool *
+ide_run_manager_get_run_tool (IdeRunManager *self)
+{
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (IDE_IS_RUN_TOOL (self->run_tool));
+
+ return self->run_tool;
+}
+
+void
+ide_run_manager_set_run_tool_from_plugin_info (IdeRunManager *self,
+ PeasPluginInfo *plugin_info)
{
- DiscoverState *state = data;
+ g_autoptr(IdeRunTool) no_tool = NULL;
+ PeasExtension *exten = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_MANAGER (self));
- g_assert (state->active == 0);
+ if (plugin_info != NULL)
+ exten = ide_extension_set_adapter_get_extension (self->run_tools, plugin_info);
- g_list_free_full (state->providers, g_object_unref);
- g_clear_pointer (&state->results, g_ptr_array_unref);
- g_slice_free (DiscoverState, state);
+ if (exten == NULL)
+ {
+ if (IDE_IS_NO_TOOL (self->run_tool))
+ return;
+ no_tool = ide_no_tool_new ();
+ exten = (PeasExtension *)no_tool;
+ }
+
+ if (g_set_object (&self->run_tool, IDE_RUN_TOOL (exten)))
+ {
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RUN_TOOL]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON_NAME]);
+ }
}
static void
-ide_run_manager_real_run (IdeRunManager *self,
- IdeRunner *runner)
+ide_run_manager_set_run_tool_from_module_name (IdeRunManager *self,
+ const char *name)
{
+ PeasPluginInfo *plugin_info = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_RUN_MANAGER (self));
- g_assert (IDE_IS_RUNNER (runner));
- /*
- * If the current handler has a callback specified (our default "run" handler
- * does not), then we need to allow that handler to prepare the runner.
- */
- if (self->handler != NULL && self->handler->handler != NULL)
- self->handler->handler (self, runner, self->handler->handler_data);
+ g_debug ("Looking for run-tool from module %s", name);
+
+ if (!ide_str_empty0 (name))
+ plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), name);
+
+ ide_run_manager_set_run_tool_from_plugin_info (self, plugin_info);
}
static void
-ide_run_handler_info_free (gpointer data)
+ide_run_manager_actions_high_contrast (IdeRunManager *self,
+ GVariant *param)
{
- IdeRunHandlerInfo *info = data;
+ GVariant *state;
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
- g_free (info->id);
- g_free (info->title);
- g_free (info->icon_name);
- g_free (info->accel);
+ state = ide_run_manager_get_action_state (self, "high-contrast");
+ ide_run_manager_set_action_state (self,
+ "high-contrast",
+ g_variant_new_boolean (!g_variant_get_boolean (state)));
+}
- if (info->handler_data_destroy)
- info->handler_data_destroy (info->handler_data);
+static void
+ide_run_manager_actions_text_direction (IdeRunManager *self,
+ GVariant *param)
+{
+ const char *str;
- g_slice_free (IdeRunHandlerInfo, info);
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (param != NULL);
+ g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
+
+ str = g_variant_get_string (param, NULL);
+ if (g_strv_contains (IDE_STRV_INIT ("ltr", "rtl"), str))
+ ide_run_manager_set_action_state (self,
+ "text-direction",
+ g_variant_new_string (str));
}
static void
-ide_run_manager_dispose (GObject *object)
+ide_run_manager_actions_color_scheme (IdeRunManager *self,
+ GVariant *param)
{
- IdeRunManager *self = (IdeRunManager *)object;
+ const char *str;
- self->handler = NULL;
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (param != NULL);
+ g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
- g_clear_object (&self->cancellable);
- ide_clear_and_destroy_object (&self->build_target);
+ str = g_variant_get_string (param, NULL);
+ if (!g_strv_contains (IDE_STRV_INIT ("follow", "force-light", "force-dark"), str))
+ str = "follow";
- g_list_free_full (self->handlers, ide_run_handler_info_free);
- self->handlers = NULL;
+ ide_run_manager_set_action_state (self,
+ "color-scheme",
+ g_variant_new_string (str));
+}
- G_OBJECT_CLASS (ide_run_manager_parent_class)->dispose (object);
+static void
+ide_run_manager_actions_default_run_command (IdeRunManager *self,
+ GVariant *param)
+{
+ const char *str;
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (param != NULL);
+ g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
+
+ str = g_variant_get_string (param, NULL);
+ if (ide_str_empty0 (str))
+ str = NULL;
+
+ if (g_strcmp0 (str, self->default_run_command) != 0)
+ {
+ g_free (self->default_run_command);
+ self->default_run_command = g_strdup (str);
+ ide_run_manager_set_action_state (self,
+ "default-run-command",
+ g_variant_new_string (str ? str : ""));
+ }
}
static void
@@ -180,10 +272,65 @@ ide_run_manager_update_action_enabled (IdeRunManager *self)
can_build = ide_build_manager_get_can_build (build_manager);
ide_run_manager_set_action_enabled (self, "run",
- self->busy == FALSE && can_build == TRUE);
+ self->busy == 0 && can_build == TRUE);
ide_run_manager_set_action_enabled (self, "run-with-handler",
- self->busy == FALSE && can_build == TRUE);
- ide_run_manager_set_action_enabled (self, "stop", self->busy == TRUE);
+ self->busy == 0 && can_build == TRUE);
+ ide_run_manager_set_action_enabled (self, "stop", self->busy > 0);
+}
+
+
+static void
+ide_run_manager_mark_busy (IdeRunManager *self)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
+
+ self->busy++;
+
+ if (self->busy == 1)
+ {
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BUSY]);
+ ide_run_manager_update_action_enabled (self);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_manager_unmark_busy (IdeRunManager *self)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
+
+ self->busy--;
+
+ if (self->busy == 0)
+ {
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BUSY]);
+ ide_run_manager_update_action_enabled (self);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_manager_dispose (GObject *object)
+{
+ IdeRunManager *self = (IdeRunManager *)object;
+
+ g_clear_pointer (&self->default_run_command, g_free);
+
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->current_run_command);
+ g_clear_object (&self->current_subprocess);
+ g_clear_object (&self->run_tool);
+
+ ide_clear_and_destroy_object (&self->run_command_providers);
+ ide_clear_and_destroy_object (&self->run_tools);
+
+ G_OBJECT_CLASS (ide_run_manager_parent_class)->dispose (object);
}
static void
@@ -202,6 +349,17 @@ ide_run_manager_notify_can_build (IdeRunManager *self,
IDE_EXIT;
}
+const char *
+ide_run_manager_get_icon_name (IdeRunManager *self)
+{
+ g_assert (IDE_IS_RUN_MANAGER (self));
+
+ if (self->run_tool == NULL)
+ return NULL;
+
+ return ide_run_tool_get_icon_name (self->run_tool);
+}
+
static gboolean
initable_init (GInitable *initable,
GCancellable *cancellable,
@@ -227,6 +385,16 @@ initable_init (GInitable *initable,
ide_run_manager_update_action_enabled (self);
+ self->run_command_providers = ide_extension_set_adapter_new (IDE_OBJECT (self),
+ peas_engine_get_default (),
+ IDE_TYPE_RUN_COMMAND_PROVIDER,
+ NULL, NULL);
+
+ self->run_tools = ide_extension_set_adapter_new (IDE_OBJECT (self),
+ peas_engine_get_default (),
+ IDE_TYPE_RUN_TOOL,
+ NULL, NULL);
+
IDE_RETURN (TRUE);
}
@@ -250,12 +418,12 @@ ide_run_manager_get_property (GObject *object,
g_value_set_boolean (value, ide_run_manager_get_busy (self));
break;
- case PROP_HANDLER:
- g_value_set_string (value, ide_run_manager_get_handler (self));
+ case PROP_ICON_NAME:
+ g_value_set_string (value, ide_run_manager_get_icon_name (self));
break;
- case PROP_BUILD_TARGET:
- g_value_set_object (value, ide_run_manager_get_build_target (self));
+ case PROP_RUN_TOOL:
+ g_value_set_object (value, ide_run_manager_get_run_tool (self));
break;
default:
@@ -263,25 +431,6 @@ ide_run_manager_get_property (GObject *object,
}
}
-static void
-ide_run_manager_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- IdeRunManager *self = IDE_RUN_MANAGER (object);
-
- switch (prop_id)
- {
- case PROP_BUILD_TARGET:
- ide_run_manager_set_build_target (self, g_value_get_object (value));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
- }
-}
-
static void
ide_run_manager_class_init (IdeRunManagerClass *klass)
{
@@ -289,78 +438,76 @@ ide_run_manager_class_init (IdeRunManagerClass *klass)
object_class->dispose = ide_run_manager_dispose;
object_class->get_property = ide_run_manager_get_property;
- object_class->set_property = ide_run_manager_set_property;
properties [PROP_BUSY] =
- g_param_spec_boolean ("busy",
- "Busy",
- "Busy",
+ g_param_spec_boolean ("busy", NULL, NULL,
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- properties [PROP_HANDLER] =
- g_param_spec_string ("handler",
- "Handler",
- "Handler",
- "run",
+ properties [PROP_ICON_NAME] =
+ g_param_spec_string ("icon-name", NULL, NULL,
+ NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- properties [PROP_BUILD_TARGET] =
- g_param_spec_object ("build-target",
- "Build Target",
- "The IdeBuildTarget that will be run",
- IDE_TYPE_BUILD_TARGET,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ properties [PROP_RUN_TOOL] =
+ g_param_spec_object ("run-tool", NULL, NULL,
+ IDE_TYPE_RUN_TOOL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
/**
* IdeRunManager::run:
* @self: An #IdeRunManager
- * @runner: An #IdeRunner
+ * @run_context: An #IdeRunContext
*
- * This signal is emitted right before ide_runner_run_async() is called
- * on an #IdeRunner. It can be used by plugins to tweak things right
- * before the runner is executed.
+ * This signal is emitted to allow plugins to add additional settings to a
+ * run context before a launcher is created.
*
- * The current run handler (debugger, profiler, etc) is run as the default
- * handler for this function. So connect with %G_SIGNAL_AFTER if you want
- * to be nofied after the run handler has executed. It's unwise to change
- * things that the run handler might expect. Generally if you want to
- * change settings, do that before the run handler has exected.
- *
- * Since: 3.32
+ * Generally this can only be used in certain situations and you probably
+ * want to modify the run context in another way such as a deploy strategry,
+ * runtime, or similar.
*/
signals [RUN] =
g_signal_new_class_handler ("run",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
- G_CALLBACK (ide_run_manager_real_run),
+ NULL,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
- IDE_TYPE_RUNNER);
+ IDE_TYPE_RUN_CONTEXT);
/**
- * IdeRunManager::stopped:
+ * IdeRunManager::started:
*
- * This signal is emitted when the run manager has stopped the currently
- * executing inferior.
+ * This signal is emitted when the run manager has spawned a new subprocess.
+ */
+ signals [STARTED] =
+ g_signal_new ("started",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * IdeRunManager::stopped:
*
- * Since: 3.32
+ * This signal is emitted when the run manager has detected the running
+ * subprocess has exited.
*/
signals [STOPPED] =
g_signal_new ("stopped",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
+ NULL, NULL,
NULL,
- NULL,
- NULL,
- G_TYPE_NONE,
- 0);
+ G_TYPE_NONE, 0);
}
gboolean
@@ -368,7 +515,7 @@ ide_run_manager_get_busy (IdeRunManager *self)
{
g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), FALSE);
- return self->busy;
+ return self->busy > 0;
}
static gboolean
@@ -392,306 +539,114 @@ ide_run_manager_check_busy (IdeRunManager *self,
}
static void
-ide_run_manager_run_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+apply_messages_debug (IdeRunContext *run_context,
+ gboolean messages_debug_all)
{
- IdeRunner *runner = (IdeRunner *)object;
- g_autoptr(IdeTask) task = user_data;
- g_autoptr(GError) error = NULL;
- IdeRunManager *self;
-
IDE_ENTRY;
- g_assert (IDE_IS_RUNNER (runner));
- g_assert (IDE_IS_TASK (task));
-
- self = ide_task_get_source_object (task);
-
- if (self->notif != NULL)
- {
- ide_notification_withdraw (self->notif);
- g_clear_object (&self->notif);
- }
-
- if (!ide_runner_run_finish (runner, result, &error))
- ide_task_return_error (task, g_steal_pointer (&error));
- else
- ide_task_return_boolean (task, TRUE);
-
- g_signal_emit (self, signals [STOPPED], 0);
+ g_assert (IDE_IS_RUN_CONTEXT (run_context));
- ide_object_destroy (IDE_OBJECT (runner));
+ if (messages_debug_all)
+ ide_run_context_setenv (run_context, "G_MESSAGES_DEBUG", "all");
IDE_EXIT;
}
static void
-copy_builtin_envvars (IdeEnvironment *environment)
-{
- static const gchar *copy_env[] = {
- "AT_SPI_BUS_ADDRESS",
- "COLORTERM",
- "DBUS_SESSION_BUS_ADDRESS",
- "DBUS_SYSTEM_BUS_ADDRESS",
- "DESKTOP_SESSION",
- "DISPLAY",
- "LANG",
- "SHELL",
- "SSH_AUTH_SOCK",
- "USER",
- "WAYLAND_DISPLAY",
- "XAUTHORITY",
- "XDG_CURRENT_DESKTOP",
- "XDG_MENU_PREFIX",
-#if 0
- /* Can't copy these as they could mess up Flatpak */
- "XDG_DATA_DIRS",
- "XDG_RUNTIME_DIR",
-#endif
- "XDG_SEAT",
- "XDG_SESSION_DESKTOP",
- "XDG_SESSION_ID",
- "XDG_SESSION_TYPE",
- "XDG_VTNR",
- };
- const gchar * const *host_environ = _ide_host_environ ();
-
- for (guint i = 0; i < G_N_ELEMENTS (copy_env); i++)
- {
- const gchar *key = copy_env[i];
- const gchar *val = g_environ_getenv ((gchar **)host_environ, key);
-
- if (val != NULL && ide_environment_getenv (environment, key) == NULL)
- ide_environment_setenv (environment, key, val);
- }
-}
-
-static void
-create_runner_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+apply_color_scheme (IdeRunContext *run_context,
+ const char *color_scheme)
{
- IdeRunManager *self;
- IdeDeviceManager *device_manager = (IDE_DEVICE_MANAGER (object));
- g_autoptr(IdeTask) task = user_data;
- g_autoptr(GError) error = NULL;
- g_autofree gchar *name = NULL;
- g_autofree gchar *title = NULL;
- IdeBuildTarget *build_target;
- IdeContext *context;
- IdeConfigManager *config_manager;
- IdeConfig *config;
- IdeEnvironment *environment;
- IdeRuntime *runtime;
- g_autoptr(IdeRunner) runner = NULL;
- GCancellable *cancellable;
- const gchar *run_opts;
-
IDE_ENTRY;
- g_assert (IDE_IS_DEVICE_MANAGER (device_manager));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_TASK (task));
-
- self = ide_task_get_source_object (task);
- g_assert (IDE_IS_RUN_MANAGER (self));
-
- runner = ide_device_manager_create_runner_finish (device_manager, result, &error);
+ g_assert (IDE_IS_RUN_CONTEXT (run_context));
+ g_assert (color_scheme != NULL);
- if (error != NULL)
- {
- ide_task_return_error (task, g_steal_pointer (&error));
- IDE_EXIT;
- }
-
- build_target = ide_task_get_task_data (task);
- context = ide_object_get_context (IDE_OBJECT (self));
-
- g_assert (IDE_IS_BUILD_TARGET (build_target));
- g_assert (IDE_IS_CONTEXT (context));
+ g_debug ("Applying color-scheme \"%s\"", color_scheme);
- config_manager = ide_config_manager_from_context (context);
- config = ide_config_manager_get_current (config_manager);
- runtime = ide_config_get_runtime (config);
-
- if (runner == NULL)
+ if (ide_str_equal0 (color_scheme, "follow"))
{
- if (runtime == NULL)
- {
- ide_task_return_new_error (task,
- IDE_RUNTIME_ERROR,
- IDE_RUNTIME_ERROR_NO_SUCH_RUNTIME,
- "%s “%s”",
- _("Failed to locate runtime"),
- ide_config_get_runtime_id (config));
- IDE_EXIT;
- }
-
- runner = ide_runtime_create_runner (runtime, build_target);
+ ide_run_context_unsetenv (run_context, "ADW_DEBUG_COLOR_SCHEME");
+ ide_run_context_unsetenv (run_context, "HDY_DEBUG_COLOR_SCHEME");
}
-
- cancellable = ide_task_get_cancellable (task);
-
- g_assert (IDE_IS_RUNNER (runner));
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- /* Add our run arguments if specified in the config. */
- if (NULL != (run_opts = ide_config_get_run_opts (config)))
+ else if (ide_str_equal0 (color_scheme, "force-light"))
{
- g_auto(GStrv) argv = NULL;
- gint argc;
-
- if (g_shell_parse_argv (run_opts, &argc, &argv, NULL))
- {
- for (gint i = 0; i < argc; i++)
- ide_runner_append_argv (runner, argv[i]);
- }
+ ide_run_context_setenv (run_context, "ADW_DEBUG_COLOR_SCHEME", "prefer-light");
+ ide_run_context_setenv (run_context, "HDY_DEBUG_COLOR_SCHEME", "prefer-light");
}
-
- /* Add our runtime environment variables. */
- environment = ide_runner_get_environment (runner);
- copy_builtin_envvars (environment);
- ide_environment_copy_into (ide_config_get_runtime_environment (config), environment, TRUE);
-
- g_signal_emit (self, signals [RUN], 0, runner);
-
- if (ide_runner_get_failed (runner))
+ else if (ide_str_equal0 (color_scheme, "force-dark"))
{
- ide_task_return_new_error (task,
- IDE_RUNTIME_ERROR,
- IDE_RUNTIME_ERROR_SPAWN_FAILED,
- "Failed to execute the application");
- IDE_EXIT;
+ ide_run_context_setenv (run_context, "ADW_DEBUG_COLOR_SCHEME", "prefer-dark");
+ ide_run_context_setenv (run_context, "HDY_DEBUG_COLOR_SCHEME", "prefer-dark");
}
-
- if (self->notif != NULL)
+ else
{
- ide_notification_withdraw (self->notif);
- g_clear_object (&self->notif);
+ g_warn_if_reached ();
}
- self->notif = ide_notification_new ();
- name = ide_build_target_get_name (build_target);
- /* translators: %s is replaced with the name of the users executable */
- title = g_strdup_printf (_("Running %s…"), name);
- ide_notification_set_title (self->notif, title);
- ide_notification_attach (self->notif, IDE_OBJECT (self));
-
- ide_runner_run_async (runner,
- cancellable,
- ide_run_manager_run_cb,
- g_object_ref (task));
-
IDE_EXIT;
}
static void
-deploy_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+apply_high_contrast (IdeRunContext *run_context,
+ gboolean high_contrast)
{
- IdeDeviceManager *device_manager = (IdeDeviceManager *)object;
- IdeBuildManager *build_manager;
- IdeContext *context;
- IdePipeline *pipeline;
- IdeRunManager *self;
- g_autoptr(IdeTask) task = user_data;
- g_autoptr(GError) error = NULL;
-
IDE_ENTRY;
- g_assert (IDE_IS_DEVICE_MANAGER (device_manager));
- g_assert (IDE_IS_TASK (task));
+ g_assert (IDE_IS_RUN_CONTEXT (run_context));
+
+ g_debug ("Applying high-contrast %d", high_contrast);
- if (!ide_device_manager_deploy_finish (device_manager, result, &error))
+ if (high_contrast)
{
- ide_task_return_error (task, g_steal_pointer (&error));
- IDE_EXIT;
+ ide_run_context_setenv (run_context, "ADW_DEBUG_HIGH_CONTRAST", "1");
+ ide_run_context_setenv (run_context, "HDY_DEBUG_HIGH_CONTRAST", "1");
+ }
+ else
+ {
+ ide_run_context_unsetenv (run_context, "ADW_DEBUG_HIGH_CONTRAST");
+ ide_run_context_unsetenv (run_context, "HDY_DEBUG_HIGH_CONTRAST");
}
-
- self = ide_task_get_source_object (task);
- g_assert (IDE_IS_RUN_MANAGER (self));
-
- context = ide_object_get_context (IDE_OBJECT (self));
- g_assert (IDE_IS_CONTEXT (context));
-
- build_manager = ide_build_manager_from_context (context);
- pipeline = ide_build_manager_get_pipeline (build_manager);
-
- ide_device_manager_create_runner_async (device_manager,
- pipeline,
- ide_task_get_cancellable (task),
- create_runner_cb,
- g_object_ref (task));
IDE_EXIT;
}
static void
-do_run_async (IdeRunManager *self,
- IdeTask *task)
+apply_text_direction (IdeRunContext *run_context,
+ const char *text_dir_str)
{
- IdeBuildManager *build_manager;
- IdeContext *context;
- IdeDeviceManager *device_manager;
- IdePipeline *pipeline;
- GCancellable *cancellable;
+ GtkTextDirection dir;
IDE_ENTRY;
- g_assert (IDE_IS_RUN_MANAGER (self));
- g_assert (IDE_IS_TASK (task));
-
- context = ide_object_get_context (IDE_OBJECT (self));
- g_assert (IDE_IS_CONTEXT (context));
-
- build_manager = ide_build_manager_from_context (context);
- pipeline = ide_build_manager_get_pipeline (build_manager);
- device_manager = ide_device_manager_from_context (context);
+ g_assert (IDE_IS_RUN_CONTEXT (run_context));
- cancellable = ide_task_get_cancellable (task);
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ if (ide_str_equal0 (text_dir_str, "rtl"))
+ dir = GTK_TEXT_DIR_RTL;
+ else if (ide_str_equal0 (text_dir_str, "ltr"))
+ dir = GTK_TEXT_DIR_LTR;
+ else
+ g_return_if_reached ();
- ide_device_manager_deploy_async (device_manager,
- pipeline,
- cancellable,
- deploy_cb,
- g_object_ref (task));
+ if (dir != gtk_widget_get_default_direction ())
+ ide_run_context_setenv (run_context, "GTK_DEBUG", "invert-text-dir");
IDE_EXIT;
}
-static void
-ide_run_manager_run_discover_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+static inline const char *
+get_action_state_string (IdeRunManager *self,
+ const char *action_name)
{
- IdeRunManager *self = (IdeRunManager *)object;
- g_autoptr(IdeBuildTarget) build_target = NULL;
- g_autoptr(IdeTask) task = user_data;
- g_autoptr(GError) error = NULL;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_RUN_MANAGER (self));
- g_assert (G_IS_ASYNC_RESULT (result));
-
- build_target = ide_run_manager_discover_default_target_finish (self, result, &error);
-
- if (build_target == NULL)
- {
- ide_task_return_error (task, g_steal_pointer (&error));
- IDE_EXIT;
- }
-
- ide_run_manager_set_build_target (self, build_target);
-
- ide_task_set_task_data (task, g_steal_pointer (&build_target), g_object_unref);
-
- do_run_async (self, task);
+ GVariant *state = ide_run_manager_get_action_state (self, action_name);
+ return g_variant_get_string (state, NULL);
+}
- IDE_EXIT;
+static inline gboolean
+get_action_state_bool (IdeRunManager *self,
+ const char *action_name)
+{
+ GVariant *state = ide_run_manager_get_action_state (self, action_name);
+ return g_variant_get_boolean (state);
}
static void
@@ -702,144 +657,402 @@ ide_run_manager_install_cb (GObject *object,
IdeBuildManager *build_manager = (IdeBuildManager *)object;
g_autoptr(IdeTask) task = user_data;
g_autoptr(GError) error = NULL;
- IdeRunManager *self;
- IdeBuildTarget *build_target;
- GCancellable *cancellable;
IDE_ENTRY;
g_assert (IDE_IS_BUILD_MANAGER (build_manager));
+ g_assert (G_IS_ASYNC_RESULT (result));
g_assert (IDE_IS_TASK (task));
- self = ide_task_get_source_object (task);
+ if (!ide_build_manager_build_finish (build_manager, result, &error))
+ ide_task_return_error (task, g_steal_pointer (&error));
+ else
+ ide_task_return_boolean (task, TRUE);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_manager_install_async (IdeRunManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeContext) context = NULL;
+ g_autoptr(GSettings) project_settings = NULL;
+ g_autoptr(IdeTask) task = NULL;
+ IdeBuildManager *build_manager;
+ IdeVcsMonitor *monitor;
+ guint64 sequence = 0;
+
+ IDE_ENTRY;
+
g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- if (!ide_build_manager_build_finish (build_manager, result, &error))
+ context = ide_object_ref_context (IDE_OBJECT (self));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_run_manager_install_async);
+
+ project_settings = ide_context_ref_project_settings (context);
+ if (!g_settings_get_boolean (project_settings, "install-before-run"))
{
- /* We want to let the consumer know there was a build error
- * (but don't need to pass the specific error code) so that
- * they have an error code to check against.
- */
- ide_task_return_new_error (task,
- IDE_RUNTIME_ERROR,
- IDE_RUNTIME_ERROR_BUILD_FAILED,
- /* translators: %s is replaced with the specific error reason */
- _("The build target failed to build: %s"),
- error->message);
+ ide_task_return_boolean (task, TRUE);
IDE_EXIT;
}
- self->last_change_seq = self->pending_last_change_seq;
-
- build_target = ide_run_manager_get_build_target (self);
+ monitor = ide_vcs_monitor_from_context (context);
+ if (monitor != NULL)
+ sequence = ide_vcs_monitor_get_sequence (monitor);
- if (build_target == NULL)
+ if (self->has_installed_once && sequence == self->last_change_seq)
{
- cancellable = ide_task_get_cancellable (task);
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- ide_run_manager_discover_default_target_async (self,
- cancellable,
- ide_run_manager_run_discover_cb,
- g_steal_pointer (&task));
+ ide_task_return_boolean (task, TRUE);
IDE_EXIT;
}
- ide_task_set_task_data (task, g_object_ref (build_target), g_object_unref);
+ self->pending_last_change_seq = sequence;
- do_run_async (self, task);
+ build_manager = ide_build_manager_from_context (context);
+ ide_build_manager_build_async (build_manager,
+ IDE_PIPELINE_PHASE_INSTALL,
+ NULL,
+ cancellable,
+ ide_run_manager_install_cb,
+ g_steal_pointer (&task));
IDE_EXIT;
}
-static void
-ide_run_manager_task_completed (IdeRunManager *self,
- GParamSpec *pspec,
- IdeTask *task)
+static gboolean
+ide_run_manager_install_finish (IdeRunManager *self,
+ GAsyncResult *result,
+ GError **error)
{
+ gboolean ret;
+
IDE_ENTRY;
g_assert (IDE_IS_RUN_MANAGER (self));
- g_assert (pspec != NULL);
+ g_assert (IDE_IS_TASK (result));
+
+ ret = ide_task_propagate_boolean (IDE_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
+
+static void
+ide_run_manager_run_subprocess_wait_check_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSubprocess *subprocess = (IdeSubprocess *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeRunManager *self;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SUBPROCESS (subprocess));
+ g_assert (G_IS_ASYNC_RESULT (result));
g_assert (IDE_IS_TASK (task));
- self->busy = FALSE;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+ self = ide_task_get_source_object (task);
+ g_assert (IDE_IS_RUN_MANAGER (self));
- ide_run_manager_update_action_enabled (self);
+ if (self->notif != NULL)
+ ide_notification_withdraw (self->notif);
+
+ g_clear_object (&self->notif);
+ g_clear_object (&self->current_subprocess);
+
+ if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
+ ide_task_return_error (task, g_steal_pointer (&error));
+ else
+ ide_task_return_boolean (task, TRUE);
+
+ if (self->run_tool != NULL)
+ _ide_run_tool_emit_stopped (self->run_tool);
+
+ g_signal_emit (self, signals[STOPPED], 0);
IDE_EXIT;
}
static void
-ide_run_manager_do_install_before_run (IdeRunManager *self,
- IdeTask *task)
+ide_run_manager_prepare_run_context (IdeRunManager *self,
+ IdeRunContext *run_context,
+ IdeRunCommand *run_command,
+ IdePipeline *pipeline)
{
- g_autoptr(IdeContext) context = NULL;
- IdeBuildManager *build_manager;
- IdeVcsMonitor *monitor;
- guint64 sequence = 0;
+ g_auto(GStrv) environ = NULL;
IDE_ENTRY;
- g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_RUN_MANAGER (self));
- g_assert (IDE_IS_TASK (task));
+ g_assert (IDE_IS_RUN_CONTEXT (run_context));
+ g_assert (IDE_IS_RUN_COMMAND (run_command));
+ g_assert (IDE_IS_PIPELINE (pipeline));
+ g_assert (IDE_IS_RUN_TOOL (self->run_tool));
- context = ide_object_ref_context (IDE_OBJECT (self));
- build_manager = ide_build_manager_from_context (context);
- monitor = ide_vcs_monitor_from_context (context);
+ g_debug ("Preparing run context using run tool %s",
+ G_OBJECT_TYPE_NAME (self->run_tool));
- /*
- * First we need to make sure the target is up to date and installed
- * so that all the dependent resources are available.
+ /* The very first thing we need to do is allow the current run tool
+ * to inject any command wrapper it needs. This might be something like
+ * gdb, or valgrind, etc.
*/
+ ide_run_tool_prepare_to_run (self->run_tool, pipeline, run_command, run_context);
- self->busy = TRUE;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+ /* Now push a new layer so that we can keep those values separate from
+ * what is configured in the run command. We use an expansion layer so
+ * that we can expand common variables at this layer and not allow them
+ * to be visible at lower layers.
+ */
+ environ = g_environ_setenv (environ, "BUILDDIR", ide_pipeline_get_builddir (pipeline), TRUE);
+ environ = g_environ_setenv (environ, "SRCDIR", ide_pipeline_get_srcdir (pipeline), TRUE);
+ environ = g_environ_setenv (environ, "HOME", g_get_home_dir (), TRUE);
+ environ = g_environ_setenv (environ, "USER", g_get_user_name (), TRUE);
+ ide_run_context_push_expansion (run_context, (const char * const *)environ);
- g_signal_connect_object (task,
- "notify::completed",
- G_CALLBACK (ide_run_manager_task_completed),
- self,
- G_CONNECT_SWAPPED);
+ /* Setup working directory */
+ {
+ const char *cwd = ide_run_command_get_cwd (run_command);
- if (monitor != NULL)
- sequence = ide_vcs_monitor_get_sequence (monitor);
+ if (cwd != NULL)
+ ide_run_context_set_cwd (run_context, cwd);
+ }
- /*
- * If we detect that nothing has changed in the project directory since the
- * last time we ran, we can avoid installing the project. This will not help
- * in situations where external resources have changed outside of builders
- * control, but users can simply force a Build in that case.
+ /* Setup command arguments */
+ {
+ const char * const *argv = ide_run_command_get_argv (run_command);
+
+ if (argv != NULL)
+ ide_run_context_append_args (run_context, argv);
+ }
+
+ /* Setup command environment */
+ {
+ const char * const *env = ide_run_command_get_environ (run_command);
+
+ if (env != NULL && env[0] != NULL)
+ ide_run_context_add_environ (run_context, env);
+ }
+
+ /* Now overlay runtime-tweaks as needed. Put this in a layer so that
+ * we can debug where things are set/changed to help us when we need
+ * to track down bugs in handlers/runtimes/devices/etc. All of our
+ * changes will get persisted to the lower layer when merging anyway.
+ *
+ * TODO: These could probably be moved into a plugin rather than in
+ * the foundry itself. That way they can be disabled by users who are
+ * doing nothing with GTK/GNOME applications.
*/
- if (self->build_target != NULL && sequence == self->last_change_seq)
+ ide_run_context_push (run_context, NULL, NULL, NULL);
+ apply_color_scheme (run_context, get_action_state_string (self, "color-scheme"));
+ apply_high_contrast (run_context, get_action_state_bool (self, "high-contrast"));
+ apply_text_direction (run_context, get_action_state_string (self, "text-direction"));
+ apply_messages_debug (run_context, self->messages_debug_all);
+
+ /* Allow plugins to track anything in the mix. For example the
+ * terminal plugin will attach a PTY here for stdin/stdout/stderr.
+ */
+ g_signal_emit (self, signals [RUN], 0, run_context);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_manager_run_deploy_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeDeployStrategy *deploy_strategy = (IdeDeployStrategy *)object;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
+ g_autoptr(IdeRunContext) run_context = NULL;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeNotification *notif;
+ IdeRunManager *self;
+ IdePipeline *pipeline;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_DEPLOY_STRATEGY (deploy_strategy));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ self = ide_task_get_source_object (task);
+ pipeline = ide_task_get_task_data (task);
+ notif = g_object_get_data (G_OBJECT (deploy_strategy), "PROGRESS");
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (IDE_IS_PIPELINE (pipeline));
+ g_assert (IDE_IS_NOTIFICATION (notif));
+
+ /* Withdraw our deploy notification */
+ ide_notification_withdraw (notif);
+ ide_object_destroy (IDE_OBJECT (notif));
+
+ if (!ide_deploy_strategy_deploy_finish (deploy_strategy, result, &error))
{
- g_debug ("Skipping install phase as no files appear to have changed "
- "(sequence %"G_GUINT64_FORMAT")", sequence);
- ide_run_manager_update_action_enabled (self);
- ide_task_set_task_data (task, g_object_ref (self->build_target), g_object_unref);
- do_run_async (self, task);
+ ide_task_return_error (task, g_steal_pointer (&error));
IDE_EXIT;
}
- self->pending_last_change_seq = sequence;
+ if (self->current_run_command == NULL)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "The operation was cancelled");
+ IDE_EXIT;
+ }
- ide_build_manager_build_async (build_manager,
- IDE_PIPELINE_PHASE_INSTALL,
- NULL,
- ide_task_get_cancellable (task),
- ide_run_manager_install_cb,
- g_object_ref (task));
+ /* Setup the run context */
+ run_context = ide_run_context_new ();
+ ide_deploy_strategy_prepare_run_context (deploy_strategy, pipeline, run_context);
+ ide_run_manager_prepare_run_context (self, run_context, self->current_run_command, pipeline);
- ide_run_manager_update_action_enabled (self);
+ /* Now spawn the subprocess or bail if there was a failure to build command */
+ if (!(subprocess = ide_run_context_spawn (run_context, &error)))
+ {
+ ide_task_return_error (task, g_steal_pointer (&error));
+ IDE_EXIT;
+ }
+
+ /* Keep subprocess around for send_signal/force_exit */
+ g_set_object (&self->current_subprocess, subprocess);
+
+ if (self->notif != NULL)
+ ide_notification_withdraw (self->notif);
+
+ /* Setup notification */
+ {
+ const char *name = ide_run_command_get_display_name (self->current_run_command);
+ /* translators: %s is replaced with the name of the users run command */
+ g_autofree char *title = g_strdup_printf (_("Running %s…"), name);
+
+ g_clear_object (&self->notif);
+ self->notif = g_object_new (IDE_TYPE_NOTIFICATION,
+ "id", "org.gnome.builder.run-manager.run",
+ "title", title,
+ NULL);
+ ide_notification_attach (self->notif, IDE_OBJECT (self));
+ }
+
+ _ide_run_tool_emit_started (self->run_tool, subprocess);
+
+ g_signal_emit (self, signals[STARTED], 0);
+
+ /* Wait for the application to finish running */
+ ide_subprocess_wait_check_async (subprocess,
+ ide_task_get_cancellable (task),
+ ide_run_manager_run_subprocess_wait_check_cb,
+ g_object_ref (task));
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_manager_run_discover_run_command_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRunManager *self = (IdeRunManager *)object;
+ g_autoptr(IdeNotification) notif = NULL;
+ g_autoptr(IdeRunCommand) run_command = NULL;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeDeployStrategy *deploy_strategy;
+ GCancellable *cancellable;
+ IdePipeline *pipeline;
+ IdeContext *context;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!(run_command = ide_run_manager_discover_run_command_finish (self, result, &error)))
+ {
+ ide_task_return_error (task, g_steal_pointer (&error));
+ IDE_EXIT;
+ }
+
+ g_set_object (&self->current_run_command, run_command);
+
+ cancellable = ide_task_get_cancellable (task);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ pipeline = ide_task_get_task_data (task);
+ g_assert (IDE_IS_PIPELINE (pipeline));
+
+ context = ide_object_get_context (IDE_OBJECT (pipeline));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ deploy_strategy = ide_pipeline_get_deploy_strategy (pipeline);
+ g_assert (IDE_IS_DEPLOY_STRATEGY (deploy_strategy));
+
+ notif = g_object_new (IDE_TYPE_NOTIFICATION,
+ "id", "org.gnome.builder.run-manager.deploy",
+ "title", _("Deploying to device…"),
+ "icon-name", "package-x-generic-symbolic",
+ "has-progress", TRUE,
+ "progress-is-imprecise", FALSE,
+ NULL);
+ ide_notification_attach (notif, IDE_OBJECT (context));
+ g_object_set_data_full (G_OBJECT (deploy_strategy),
+ "PROGRESS",
+ g_object_ref (notif),
+ g_object_unref);
+
+ ide_deploy_strategy_deploy_async (deploy_strategy,
+ pipeline,
+ ide_notification_file_progress_callback,
+ g_object_ref (notif),
+ g_object_unref,
+ cancellable,
+ ide_run_manager_run_deploy_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_manager_run_install_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRunManager *self = (IdeRunManager *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!ide_run_manager_install_finish (self, result, &error))
+ ide_task_return_error (task, g_steal_pointer (&error));
+ else
+ ide_run_manager_discover_run_command_async (self,
+ ide_task_get_cancellable (task),
+ ide_run_manager_run_discover_run_command_cb,
+ g_object_ref (task));
IDE_EXIT;
}
void
ide_run_manager_run_async (IdeRunManager *self,
- IdeBuildTarget *build_target,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -847,23 +1060,25 @@ ide_run_manager_run_async (IdeRunManager *self,
g_autoptr(IdeTask) task = NULL;
g_autoptr(GCancellable) local_cancellable = NULL;
g_autoptr(GError) error = NULL;
+ IdeBuildManager *build_manager;
+ IdePipeline *pipeline;
+ IdeContext *context;
IDE_ENTRY;
g_return_if_fail (IDE_IS_MAIN_THREAD ());
g_return_if_fail (IDE_IS_RUN_MANAGER (self));
- g_return_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (!g_cancellable_is_cancelled (self->cancellable));
if (cancellable == NULL)
cancellable = local_cancellable = g_cancellable_new ();
+ ide_cancellable_chain (cancellable, self->cancellable);
- dzl_cancellable_chain (cancellable, self->cancellable);
+ self->sent_signal = FALSE;
task = ide_task_new (self, cancellable, callback, user_data);
ide_task_set_source_tag (task, ide_run_manager_run_async);
- ide_task_set_priority (task, G_PRIORITY_LOW);
if (ide_task_return_error_if_cancelled (task))
IDE_EXIT;
@@ -874,10 +1089,32 @@ ide_run_manager_run_async (IdeRunManager *self,
IDE_EXIT;
}
- if (build_target != NULL)
- ide_run_manager_set_build_target (self, build_target);
+ ide_run_manager_mark_busy (self);
+ g_signal_connect_object (task,
+ "notify::completed",
+ G_CALLBACK (ide_run_manager_unmark_busy),
+ self,
+ G_CONNECT_SWAPPED);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ build_manager = ide_build_manager_from_context (context);
+ pipeline = ide_build_manager_get_pipeline (build_manager);
+
+ if (pipeline == NULL)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ "A pipeline cannot be found");
+ IDE_EXIT;
+ }
+
+ ide_task_set_task_data (task, g_object_ref (pipeline), g_object_unref);
- ide_run_manager_do_install_before_run (self, task);
+ ide_run_manager_install_async (self,
+ cancellable,
+ ide_run_manager_run_install_cb,
+ g_steal_pointer (&task));
IDE_EXIT;
}
@@ -914,13 +1151,54 @@ do_cancel_in_timeout (gpointer user_data)
IDE_RETURN (G_SOURCE_REMOVE);
}
+static int
+ide_run_manager_get_exit_signal (IdeRunManager *self)
+{
+ g_autoptr(GSettings) settings = NULL;
+ g_autofree char *stop_signal = NULL;
+ IdeContext *context;
+ int signum;
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ settings = ide_context_ref_project_settings (context);
+ stop_signal = g_settings_get_string (settings, "stop-signal");
+
+ if (0) {}
+ else if (ide_str_equal0 (stop_signal, "SIGKILL")) signum = SIGKILL;
+ else if (ide_str_equal0 (stop_signal, "SIGINT")) signum = SIGINT;
+ else if (ide_str_equal0 (stop_signal, "SIGHUP")) signum = SIGHUP;
+ else if (ide_str_equal0 (stop_signal, "SIGUSR1")) signum = SIGUSR1;
+ else if (ide_str_equal0 (stop_signal, "SIGUSR2")) signum = SIGUSR2;
+ else if (ide_str_equal0 (stop_signal, "SIGABRT")) signum = SIGABRT;
+ else if (ide_str_equal0 (stop_signal, "SIGQUIT")) signum = SIGQUIT;
+ else signum = SIGKILL;
+
+ return signum;
+}
+
void
ide_run_manager_cancel (IdeRunManager *self)
{
IDE_ENTRY;
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+ if (self->current_subprocess != NULL)
+ {
+ int exit_signal = ide_run_manager_get_exit_signal (self);
+
+ if (!self->sent_signal)
+ ide_run_tool_send_signal (self->run_tool, exit_signal);
+ else
+ ide_run_tool_force_exit (self->run_tool);
+
+ self->sent_signal = TRUE;
+ }
+
+ /* Make sure tasks are cancelled too */
if (self->cancellable != NULL)
g_timeout_add (0, do_cancel_in_timeout, g_steal_pointer (&self->cancellable));
self->cancellable = g_cancellable_new ();
@@ -928,444 +1206,382 @@ ide_run_manager_cancel (IdeRunManager *self)
IDE_EXIT;
}
-void
-ide_run_manager_set_handler (IdeRunManager *self,
- const gchar *id)
+static void
+ide_run_manager_run_action_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+ IdeRunManager *self = (IdeRunManager *)object;
+ IdeContext *context;
+ g_autoptr(GError) error = NULL;
- self->handler = NULL;
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
- for (GList *iter = self->handlers; iter; iter = iter->next)
- {
- const IdeRunHandlerInfo *info = iter->data;
+ context = ide_object_get_context (IDE_OBJECT (self));
- if (g_strcmp0 (info->id, id) == 0)
- {
- self->handler = info;
- IDE_TRACE_MSG ("run handler set to %s", info->title);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HANDLER]);
- break;
- }
- }
+ /* Propagate the error to the context */
+ if (!ide_run_manager_run_finish (self, result, &error))
+ ide_context_warning (context, "%s", error->message);
}
-void
-ide_run_manager_add_handler (IdeRunManager *self,
- const gchar *id,
- const gchar *title,
- const gchar *icon_name,
- const gchar *accel,
- IdeRunHandler run_handler,
- gpointer user_data,
- GDestroyNotify user_data_destroy)
+static void
+ide_run_manager_actions_run (IdeRunManager *self,
+ GVariant *param)
{
- IdeRunHandlerInfo *info;
- DzlShortcutManager *manager;
- DzlShortcutTheme *theme;
- g_autofree gchar *action_name = NULL;
- GApplication *app;
+ IDE_ENTRY;
- g_return_if_fail (IDE_IS_RUN_MANAGER (self));
- g_return_if_fail (id != NULL);
- g_return_if_fail (title != NULL);
-
- info = g_slice_new0 (IdeRunHandlerInfo);
- info->id = g_strdup (id);
- info->title = g_strdup (title);
- info->icon_name = g_strdup (icon_name);
- info->accel = g_strdup (accel);
- info->handler = run_handler;
- info->handler_data = user_data;
- info->handler_data_destroy = user_data_destroy;
-
- self->handlers = g_list_append (self->handlers, info);
-
- app = g_application_get_default ();
- manager = dzl_application_get_shortcut_manager (DZL_APPLICATION (app));
- theme = g_object_ref (dzl_shortcut_manager_get_theme (manager));
-
- action_name = g_strdup_printf ("run-manager.run-with-handler('%s')", id);
-
- dzl_shortcut_manager_add_action (manager,
- action_name,
- N_("Workbench shortcuts"),
- N_("Build and Run"),
- g_dgettext (GETTEXT_PACKAGE, title),
- NULL);
-
- dzl_shortcut_theme_set_accel_for_action (theme,
- action_name,
- accel,
- DZL_SHORTCUT_PHASE_GLOBAL | DZL_SHORTCUT_PHASE_CAPTURE);
-
- if (self->handler == NULL)
- self->handler = info;
+ g_assert (IDE_IS_RUN_MANAGER (self));
+
+ ide_run_manager_run_async (self,
+ NULL,
+ ide_run_manager_run_action_cb,
+ NULL);
+
+ IDE_EXIT;
}
-void
-ide_run_manager_remove_handler (IdeRunManager *self,
- const gchar *id)
+static void
+ide_run_manager_actions_run_with_handler (IdeRunManager *self,
+ GVariant *param)
{
- g_return_if_fail (IDE_IS_RUN_MANAGER (self));
- g_return_if_fail (id != NULL);
+ IDE_ENTRY;
- for (GList *iter = self->handlers; iter; iter = iter->next)
- {
- IdeRunHandlerInfo *info = iter->data;
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
- if (g_strcmp0 (info->id, id) == 0)
- {
- self->handlers = g_list_delete_link (self->handlers, iter);
+ ide_run_manager_set_run_tool_from_module_name (self, g_variant_get_string (param, NULL));
- if (self->handler == info && self->handlers != NULL)
- self->handler = self->handlers->data;
- else
- self->handler = NULL;
+ ide_run_manager_run_async (self,
+ NULL,
+ ide_run_manager_run_action_cb,
+ NULL);
- ide_run_handler_info_free (info);
+ IDE_EXIT;
+}
- break;
- }
- }
+static void
+ide_run_manager_actions_stop (IdeRunManager *self,
+ GVariant *param)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_RUN_MANAGER (self));
+
+ ide_run_manager_cancel (self);
+
+ IDE_EXIT;
}
-/**
- * ide_run_manager_get_build_target:
- *
- * Gets the build target that will be executed by the run manager which
- * was either specified to ide_run_manager_run_async() or determined by
- * the build system.
- *
- * Returns: (transfer none): An #IdeBuildTarget or %NULL if no build target
- * has been set.
- *
- * Since: 3.32
- */
-IdeBuildTarget *
-ide_run_manager_get_build_target (IdeRunManager *self)
+static void
+ide_run_manager_init (IdeRunManager *self)
{
- g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+ GtkTextDirection text_dir;
- return self->build_target;
+ self->cancellable = g_cancellable_new ();
+ self->run_tool = ide_no_tool_new ();
+
+ /* Setup initial text direction state */
+ text_dir = gtk_widget_get_default_direction ();
+ if (text_dir == GTK_TEXT_DIR_LTR)
+ ide_run_manager_set_action_state (self,
+ "text-direction",
+ text_dir == GTK_TEXT_DIR_LTR ?
+ g_variant_new_string ("ltr") :
+ g_variant_new_string ("rtl"));
}
void
-ide_run_manager_set_build_target (IdeRunManager *self,
- IdeBuildTarget *build_target)
+_ide_run_manager_drop_caches (IdeRunManager *self)
{
g_return_if_fail (IDE_IS_RUN_MANAGER (self));
- g_return_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target));
- if (build_target == self->build_target)
- return;
+ self->last_change_seq = 0;
+}
+
+static void
+ide_run_manager_actions_messages_debug_all (IdeRunManager *self,
+ GVariant *param)
+{
+ IDE_ENTRY;
- if (self->build_target)
- ide_clear_and_destroy_object (&self->build_target);
+ g_assert (IDE_IS_RUN_MANAGER (self));
- if (build_target)
- self->build_target = g_object_ref (build_target);
+ self->messages_debug_all = !self->messages_debug_all;
+ ide_run_manager_set_action_state (self,
+ "messages-debug-all",
+ g_variant_new_boolean (self->messages_debug_all));
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUILD_TARGET]);
+ IDE_EXIT;
}
-static gint
-compare_targets (gconstpointer a,
- gconstpointer b)
+typedef struct
{
- const IdeBuildTarget * const *a_target = a;
- const IdeBuildTarget * const *b_target = b;
-
- return ide_build_target_compare (*a_target, *b_target);
-}
+ GString *errors;
+ GListStore *store;
+ int n_active;
+} ListCommands;
static void
-collect_extensions (PeasExtensionSet *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+list_commands_free (ListCommands *state)
{
- DiscoverState *state = user_data;
-
g_assert (state != NULL);
- g_assert (IDE_IS_BUILD_TARGET_PROVIDER (exten));
+ g_assert (state->n_active == 0);
- state->providers = g_list_append (state->providers, g_object_ref (exten));
- state->active++;
+ g_string_free (state->errors, TRUE);
+ state->errors = NULL;
+ g_clear_object (&state->store);
+ g_slice_free (ListCommands, state);
}
static void
-ide_run_manager_provider_get_targets_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+ide_run_manager_list_commands_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- IdeBuildTargetProvider *provider = (IdeBuildTargetProvider *)object;
- g_autoptr(IdeBuildTarget) first = NULL;
+ IdeRunCommandProvider *provider = (IdeRunCommandProvider *)object;
+ g_autoptr(GListModel) model = NULL;
g_autoptr(IdeTask) task = user_data;
- g_autoptr(GPtrArray) ret = NULL;
g_autoptr(GError) error = NULL;
- IdeRunManager *self;
- DiscoverState *state;
-
- IDE_ENTRY;
+ ListCommands *state;
- g_assert (IDE_IS_BUILD_TARGET_PROVIDER (provider));
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (IDE_IS_TASK (task));
- self = ide_task_get_source_object (task);
state = ide_task_get_task_data (task);
- g_assert (IDE_IS_RUN_MANAGER (self));
g_assert (state != NULL);
- g_assert (state->active > 0);
- g_assert (g_list_find (state->providers, provider) != NULL);
+ g_assert (state->n_active > 0);
+ g_assert (G_IS_LIST_STORE (state->store));
- ret = ide_build_target_provider_get_targets_finish (provider, result, &error);
- IDE_PTR_ARRAY_SET_FREE_FUNC (ret, g_object_unref);
-
- if (ret != NULL)
+ if (!(model = ide_run_command_provider_list_commands_finish (provider, result, &error)))
{
- for (guint i = 0; i < ret->len; i++)
+ if (!ide_error_ignore (error))
{
- IdeBuildTarget *target = g_ptr_array_index (ret, i);
-
- if (ide_object_is_root (IDE_OBJECT (target)))
- ide_object_append (IDE_OBJECT (self), IDE_OBJECT (target));
-
- g_ptr_array_add (state->results, g_object_ref (target));
+ if (state->errors->len > 0)
+ g_string_append (state->errors, "; ");
+ g_string_append (state->errors, error->message);
}
}
+ else
+ {
+ g_list_store_append (state->store, model);
+ }
- ide_object_destroy (IDE_OBJECT (provider));
-
- state->active--;
-
- if (state->active > 0)
- return;
+ state->n_active--;
- if (state->results->len == 0)
+ if (state->n_active == 0)
{
- if (error != NULL)
- ide_task_return_error (task, g_steal_pointer (&error));
- else
+ if (state->errors->len > 0)
ide_task_return_new_error (task,
- IDE_RUNTIME_ERROR,
- IDE_RUNTIME_ERROR_TARGET_NOT_FOUND,
- _("Failed to locate a build target"));
- IDE_EXIT;
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "%s",
+ state->errors->str);
+ else
+ ide_task_return_pointer (task,
+ gtk_flatten_list_model_new (G_LIST_MODEL (g_steal_pointer (&state->store))),
+ g_object_unref);
}
+}
- g_ptr_array_sort (state->results, compare_targets);
+static void
+ide_run_manager_list_commands_foreach_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeRunCommandProvider *provider = (IdeRunCommandProvider *)exten;
+ IdeTask *task = user_data;
+ ListCommands *state;
- /* Steal the first item so that it is not destroyed */
- first = ide_ptr_array_steal_index (state->results,
- 0,
- (GDestroyNotify)ide_object_unref_and_destroy);
- ide_task_return_pointer (task,
- IDE_OBJECT (g_steal_pointer (&first)),
- ide_object_unref_and_destroy);
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
+ g_assert (IDE_IS_TASK (task));
- IDE_EXIT;
+ state = ide_task_get_task_data (task);
+ state->n_active++;
+
+ ide_run_command_provider_list_commands_async (provider,
+ ide_task_get_cancellable (task),
+ ide_run_manager_list_commands_cb,
+ g_object_ref (task));
}
void
-ide_run_manager_discover_default_target_async (IdeRunManager *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ide_run_manager_list_commands_async (IdeRunManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- g_autoptr(PeasExtensionSet) set = NULL;
g_autoptr(IdeTask) task = NULL;
- DiscoverState *state;
+ ListCommands *state;
IDE_ENTRY;
g_return_if_fail (IDE_IS_RUN_MANAGER (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- task = ide_task_new (self, cancellable, callback, user_data);
- ide_task_set_source_tag (task, ide_run_manager_discover_default_target_async);
- ide_task_set_priority (task, G_PRIORITY_LOW);
+ state = g_slice_new0 (ListCommands);
+ state->store = g_list_store_new (G_TYPE_LIST_MODEL);
+ state->errors = g_string_new (NULL);
- set = peas_extension_set_new (peas_engine_get_default (),
- IDE_TYPE_BUILD_TARGET_PROVIDER,
- NULL);
-
- state = g_slice_new0 (DiscoverState);
- state->results = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_object_unref_and_destroy);
- state->providers = NULL;
- state->active = 0;
-
- peas_extension_set_foreach (set, collect_extensions, state);
-
- for (const GList *iter = state->providers; iter; iter = iter->next)
- ide_object_append (IDE_OBJECT (self), IDE_OBJECT (iter->data));
-
- ide_task_set_task_data (task, state, discover_state_free);
-
- for (const GList *iter = state->providers; iter != NULL; iter = iter->next)
- {
- IdeBuildTargetProvider *provider = iter->data;
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_run_manager_list_commands_async);
+ ide_task_set_task_data (task, state, list_commands_free);
- ide_build_target_provider_get_targets_async (provider,
- cancellable,
- ide_run_manager_provider_get_targets_cb,
- g_object_ref (task));
- }
+ if (self->run_command_providers)
+ ide_extension_set_adapter_foreach (self->run_command_providers,
+ ide_run_manager_list_commands_foreach_cb,
+ task);
- if (state->active == 0)
+ if (state->n_active == 0)
ide_task_return_new_error (task,
- IDE_RUNTIME_ERROR,
- IDE_RUNTIME_ERROR_TARGET_NOT_FOUND,
- _("Failed to locate a build target"));
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "No run command providers available");
IDE_EXIT;
}
/**
- * ide_run_manager_discover_default_target_finish:
+ * ide_run_manager_list_commands_finish:
*
- * Returns: (transfer full): An #IdeBuildTarget if successful; otherwise %NULL
- * and @error is set.
- *
- * Since: 3.32
+ * Returns: (transfer full): a #GListModel of #IdeRunCommand
*/
-IdeBuildTarget *
-ide_run_manager_discover_default_target_finish (IdeRunManager *self,
- GAsyncResult *result,
- GError **error)
+GListModel *
+ide_run_manager_list_commands_finish (IdeRunManager *self,
+ GAsyncResult *result,
+ GError **error)
{
- IdeBuildTarget *ret;
-
- IDE_ENTRY;
-
g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
g_return_val_if_fail (IDE_IS_TASK (result), NULL);
- ret = ide_task_propagate_pointer (IDE_TASK (result), error);
-
- IDE_RETURN (ret);
-}
-
-const GList *
-_ide_run_manager_get_handlers (IdeRunManager *self)
-{
- g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
-
- return self->handlers;
-}
-
-const gchar *
-ide_run_manager_get_handler (IdeRunManager *self)
-{
- g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
-
- if (self->handler != NULL)
- return self->handler->id;
-
- return NULL;
+ return ide_task_propagate_pointer (IDE_TASK (result), error);
}
static void
-ide_run_manager_run_action_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+ide_run_manager_discover_run_command_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
IdeRunManager *self = (IdeRunManager *)object;
- IdeContext *context;
+ g_autoptr(IdeRunCommand) best = NULL;
+ g_autoptr(GListModel) model = NULL;
+ g_autoptr(IdeTask) task = user_data;
g_autoptr(GError) error = NULL;
+ const char *default_id;
+ guint n_items;
+ int best_priority = G_MAXINT;
- g_assert (IDE_IS_RUN_MANAGER (self));
- g_assert (G_IS_ASYNC_RESULT (result));
-
- context = ide_object_get_context (IDE_OBJECT (self));
-
- /* Propagate the error to the context */
- if (!ide_run_manager_run_finish (self, result, &error))
- ide_context_warning (context, "%s", error->message);
-}
-
-static void
-ide_run_manager_actions_run (IdeRunManager *self,
- GVariant *param)
-{
IDE_ENTRY;
g_assert (IDE_IS_RUN_MANAGER (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
- ide_run_manager_run_async (self,
- NULL,
- NULL,
- ide_run_manager_run_action_cb,
- NULL);
+ if (!(model = ide_run_manager_list_commands_finish (self, result, &error)))
+ {
+ ide_task_return_error (task, g_steal_pointer (&error));
+ IDE_EXIT;
+ }
- IDE_EXIT;
-}
+ default_id = ide_task_get_task_data (task);
+ n_items = g_list_model_get_n_items (model);
-static void
-ide_run_manager_actions_run_with_handler (IdeRunManager *self,
- GVariant *param)
-{
- const gchar *handler = NULL;
- g_autoptr(GVariant) sunk = NULL;
+ for (guint i = 0; i < n_items; i++)
+ {
+ g_autoptr(IdeRunCommand) run_command = g_list_model_get_item (model, i);
+ const char *id;
+ int priority;
- IDE_ENTRY;
+ g_assert (IDE_IS_RUN_COMMAND (run_command));
- g_assert (IDE_IS_RUN_MANAGER (self));
+ id = ide_run_command_get_id (run_command);
+ priority = ide_run_command_get_priority (run_command);
- if (param != NULL)
- {
- handler = g_variant_get_string (param, NULL);
- if (g_variant_is_floating (param))
- sunk = g_variant_ref_sink (param);
- }
+ if (!ide_str_empty0 (id) &&
+ !ide_str_empty0 (default_id) &&
+ strcmp (default_id, id) == 0)
+ {
+ ide_task_return_pointer (task,
+ g_steal_pointer (&run_command),
+ g_object_unref);
+ IDE_EXIT;
+ }
- /* Use specified handler, if provided */
- if (!ide_str_empty0 (handler))
- ide_run_manager_set_handler (self, handler);
+ if (best == NULL || priority < best_priority)
+ {
+ g_set_object (&best, run_command);
+ best_priority = priority;
+ }
+ }
- ide_run_manager_run_async (self,
- NULL,
- NULL,
- ide_run_manager_run_action_cb,
- NULL);
+ if (best != NULL)
+ ide_task_return_pointer (task,
+ g_steal_pointer (&best),
+ g_object_unref);
+ else
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ "No run command discovered. Set one manually.");
IDE_EXIT;
}
-static void
-ide_run_manager_actions_stop (IdeRunManager *self,
- GVariant *param)
+void
+ide_run_manager_discover_run_command_async (IdeRunManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ g_autoptr(IdeTask) task = NULL;
+
IDE_ENTRY;
- g_assert (IDE_IS_RUN_MANAGER (self));
+ g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- ide_run_manager_cancel (self);
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_run_manager_discover_run_command_async);
+ ide_task_set_task_data (task, g_strdup (self->default_run_command), g_free);
+
+ ide_run_manager_list_commands_async (self,
+ cancellable,
+ ide_run_manager_discover_run_command_cb,
+ g_steal_pointer (&task));
IDE_EXIT;
}
-static void
-ide_run_manager_init (IdeRunManager *self)
+/**
+ * ide_run_manager_discover_run_command_finish:
+ * @self: a #IdeRunManager
+ *
+ * Complete request to discover the default run command.
+ *
+ * Returns: (transfer full): an #IdeRunCommand if successful; otherwise
+ * %NULL and @error is set.
+ */
+IdeRunCommand *
+ide_run_manager_discover_run_command_finish (IdeRunManager *self,
+ GAsyncResult *result,
+ GError **error)
{
- self->cancellable = g_cancellable_new ();
+ IdeRunCommand *run_command;
- ide_run_manager_add_handler (self,
- "run",
- _("Run"),
- "builder-run-start-symbolic",
- "<primary>F5",
- NULL,
- NULL,
- NULL);
-}
+ IDE_ENTRY;
-void
-_ide_run_manager_drop_caches (IdeRunManager *self)
-{
- g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+ g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+ g_return_val_if_fail (IDE_IS_TASK (result), NULL);
- self->last_change_seq = 0;
+ run_command = ide_task_propagate_pointer (IDE_TASK (result), error);
+
+ g_return_val_if_fail (!run_command || IDE_IS_RUN_COMMAND (run_command), NULL);
+
+ IDE_RETURN (run_command);
}
diff --git a/src/libide/foundry/ide-run-manager.h b/src/libide/foundry/ide-run-manager.h
index 3889bb9c1..d10ab5501 100644
--- a/src/libide/foundry/ide-run-manager.h
+++ b/src/libide/foundry/ide-run-manager.h
@@ -24,6 +24,8 @@
# error "Only <libide-foundry.h> can be included directly."
#endif
+#include <libpeas/peas.h>
+
#include <libide-core.h>
#include "ide-foundry-types.h"
@@ -32,59 +34,47 @@ G_BEGIN_DECLS
#define IDE_TYPE_RUN_MANAGER (ide_run_manager_get_type())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (IdeRunManager, ide_run_manager, IDE, RUN_MANAGER, IdeObject)
-typedef void (*IdeRunHandler) (IdeRunManager *self,
- IdeRunner *runner,
- gpointer user_data);
-
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeRunManager *ide_run_manager_from_context (IdeContext *context);
-IDE_AVAILABLE_IN_3_32
-IdeBuildTarget *ide_run_manager_get_build_target (IdeRunManager *self);
-IDE_AVAILABLE_IN_3_32
-void ide_run_manager_set_build_target (IdeRunManager *self,
- IdeBuildTarget *build_target);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_run_manager_cancel (IdeRunManager *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
+const char *ide_run_manager_get_icon_name (IdeRunManager *self);
+IDE_AVAILABLE_IN_ALL
gboolean ide_run_manager_get_busy (IdeRunManager *self);
-IDE_AVAILABLE_IN_3_32
-const gchar *ide_run_manager_get_handler (IdeRunManager *self);
-IDE_AVAILABLE_IN_3_32
-void ide_run_manager_set_handler (IdeRunManager *self,
- const gchar *id);
-IDE_AVAILABLE_IN_3_32
-void ide_run_manager_add_handler (IdeRunManager *self,
- const gchar *id,
- const gchar *title,
- const gchar *icon_name,
- const gchar *accel,
- IdeRunHandler run_handler,
- gpointer user_data,
- GDestroyNotify user_data_destroy);
-IDE_AVAILABLE_IN_3_32
-void ide_run_manager_remove_handler (IdeRunManager *self,
- const gchar *id);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
+void ide_run_manager_set_run_tool_from_plugin_info (IdeRunManager *self,
+ PeasPluginInfo *plugin_info);
+IDE_AVAILABLE_IN_ALL
void ide_run_manager_run_async (IdeRunManager *self,
- IdeBuildTarget *build_target,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_run_manager_run_finish (IdeRunManager *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
-void ide_run_manager_discover_default_target_async (IdeRunManager *self,
+IDE_AVAILABLE_IN_ALL
+void ide_run_manager_list_commands_async (IdeRunManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+GListModel *ide_run_manager_list_commands_finish (IdeRunManager *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_ALL
+void ide_run_manager_discover_run_command_async (IdeRunManager *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
-IdeBuildTarget *ide_run_manager_discover_default_target_finish (IdeRunManager *self,
+IDE_AVAILABLE_IN_ALL
+IdeRunCommand *ide_run_manager_discover_run_command_finish (IdeRunManager *self,
GAsyncResult *result,
GError **error);
+
G_END_DECLS
diff --git a/src/libide/foundry/ide-runtime.c b/src/libide/foundry/ide-runtime.c
index f58b29718..c187cabc0 100644
--- a/src/libide/foundry/ide-runtime.c
+++ b/src/libide/foundry/ide-runtime.c
@@ -22,7 +22,6 @@
#include "config.h"
-#include <dazzle.h>
#include <glib/gi18n.h>
#include <string.h>
@@ -33,11 +32,13 @@
# include "../terminal/ide-terminal-util.h"
#undef IDE_TERMINAL_INSIDE
+#include "ide-build-manager.h"
#include "ide-build-target.h"
#include "ide-config.h"
#include "ide-config-manager.h"
+#include "ide-pipeline.h"
+#include "ide-run-context.h"
#include "ide-runtime.h"
-#include "ide-runner.h"
#include "ide-toolchain.h"
#include "ide-triplet.h"
@@ -64,89 +65,22 @@ enum {
static GParamSpec *properties [N_PROPS];
-static IdeSubprocessLauncher *
-ide_runtime_real_create_launcher (IdeRuntime *self,
- GError **error)
-{
- IdeSubprocessLauncher *ret;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_RUNTIME (self));
-
- ret = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE);
-
- if (ret != NULL)
- {
- ide_subprocess_launcher_set_run_on_host (ret, TRUE);
- ide_subprocess_launcher_set_clear_env (ret, FALSE);
- }
- else
- {
- g_set_error (error,
- G_IO_ERROR,
- G_IO_ERROR_FAILED,
- "An unknown error ocurred");
- }
-
- IDE_RETURN (ret);
-}
-
static gboolean
ide_runtime_real_contains_program_in_path (IdeRuntime *self,
- const gchar *program,
+ const char *program,
GCancellable *cancellable)
{
+ g_autofree char *path = NULL;
+
g_assert (IDE_IS_RUNTIME (self));
g_assert (program != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- if (!ide_is_flatpak ())
- {
- g_autofree gchar *path = NULL;
- path = g_find_program_in_path (program);
- return path != NULL;
- }
- else
- {
- g_autoptr(IdeSubprocessLauncher) launcher = NULL;
-
- /*
- * If we are in flatpak, we have to execute a program on the host to
- * determine if there is a program available, as we cannot resolve
- * file paths from inside the mount namespace.
- */
-
- if (NULL != (launcher = ide_runtime_create_launcher (self, NULL)))
- {
- g_autoptr(IdeSubprocess) subprocess = NULL;
- g_autofree char *escaped = g_shell_quote (program);
- g_autofree char *command = g_strdup_printf ("which %s", escaped);
- const char *user_shell = ide_get_user_shell ();
-
- ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
-
- /* Try to get a real PATH by using the preferred shell */
- if (ide_shell_supports_dash_c (user_shell))
- ide_subprocess_launcher_push_argv (launcher, user_shell);
- else
- ide_subprocess_launcher_push_argv (launcher, "sh");
-
- /* Try a login shell as well to improve reliability */
- if (ide_shell_supports_dash_login (user_shell))
- ide_subprocess_launcher_push_argv (launcher, "--login");
+ path = g_find_program_in_path (program);
- ide_subprocess_launcher_push_argv (launcher, "-c");
- ide_subprocess_launcher_push_argv (launcher, command);
+ IDE_TRACE_MSG ("Locating program %s => %s", program, path ? path : "missing");
- if (NULL != (subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, NULL)))
- return ide_subprocess_wait_check (subprocess, NULL, NULL);
- }
-
- return FALSE;
- }
-
- g_assert_not_reached ();
+ return path != NULL;
}
gboolean
@@ -199,130 +133,22 @@ ide_runtime_real_prepare_configuration (IdeRuntime *self,
}
}
-static IdeRunner *
-ide_runtime_real_create_runner (IdeRuntime *self,
- IdeBuildTarget *build_target)
+static GFile *
+ide_runtime_null_translate_file (IdeRuntime *self,
+ GFile *file)
{
- IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
- IdeEnvironment *env;
- g_autoptr(GFile) installdir = NULL;
- g_auto(GStrv) argv = NULL;
- g_autofree gchar *cwd = NULL;
- IdeConfigManager *config_manager;
- const gchar *prefix;
- IdeContext *context;
- IdeRunner *runner;
- IdeConfig *config;
-
- g_assert (IDE_IS_RUNTIME (self));
- g_assert (!build_target || IDE_IS_BUILD_TARGET (build_target));
-
- context = ide_object_get_context (IDE_OBJECT (self));
- g_assert (IDE_IS_CONTEXT (context));
-
- config_manager = ide_config_manager_from_context (context);
- config = ide_config_manager_get_current (config_manager);
- prefix = ide_config_get_prefix (config);
-
- runner = ide_runner_new (context);
- g_assert (IDE_IS_RUNNER (runner));
-
- ide_object_append (IDE_OBJECT (self), IDE_OBJECT (runner));
-
- env = ide_runner_get_environment (runner);
-
- if (ide_str_equal0 (priv->id, "host"))
- ide_runner_set_run_on_host (runner, TRUE);
-
- if (build_target != NULL)
- {
- ide_runner_set_build_target (runner, build_target);
-
- installdir = ide_build_target_get_install_directory (build_target);
- argv = ide_build_target_get_argv (build_target);
- cwd = ide_build_target_get_cwd (build_target);
- }
-
- /* Possibly translate relative paths for the binary */
- if (argv && argv[0] && !g_path_is_absolute (argv[0]))
- {
- const gchar *slash = strchr (argv[0], '/');
-
- if (slash != NULL)
- {
- g_autofree gchar *copy = g_strdup (slash ? (slash + 1) : argv[0]);
-
- g_free (argv[0]);
-
- if (installdir != NULL)
- {
- g_autoptr(GFile) dest = g_file_get_child (installdir, copy);
- argv[0] = g_file_get_path (dest);
- }
- else
- argv[0] = g_steal_pointer (©);
- }
- }
-
- if (installdir != NULL)
- {
- g_autoptr(GFile) parentdir = NULL;
- g_autofree gchar *schemadir = NULL;
- g_autofree gchar *parentpath = NULL;
-
- /* GSettings requires an env var for non-standard dirs */
- if ((parentdir = g_file_get_parent (installdir)))
- {
- parentpath = g_file_get_path (parentdir);
- schemadir = g_build_filename (parentpath, "share", "glib-2.0", "schemas", NULL);
- ide_environment_setenv (env, "GSETTINGS_SCHEMA_DIR", schemadir);
- }
- }
-
- if (prefix != NULL)
- {
- static const gchar *tries[] = { "lib64", "lib", "lib32", };
- const gchar *old_path = ide_environment_getenv (env, "LD_LIBRARY_PATH");
-
- for (guint i = 0; i < G_N_ELEMENTS (tries); i++)
- {
- g_autofree gchar *ld_library_path = g_build_filename (prefix, tries[i], NULL);
-
- if (g_file_test (ld_library_path, G_FILE_TEST_IS_DIR))
- {
- if (old_path != NULL)
- {
- g_autofree gchar *freeme = g_steal_pointer (&ld_library_path);
- ld_library_path = g_strdup_printf ("%s:%s", freeme, old_path);
- }
-
- ide_environment_setenv (env, "LD_LIBRARY_PATH", ld_library_path);
- break;
- }
- }
- }
-
- if (argv != NULL)
- ide_runner_push_args (runner, (const gchar * const *)argv);
-
- if (cwd != NULL)
- ide_runner_set_cwd (runner, cwd);
-
- return runner;
+ return NULL;
}
static GFile *
-ide_runtime_real_translate_file (IdeRuntime *self,
- GFile *file)
+ide_runtime_flatpak_translate_file (IdeRuntime *self,
+ GFile *file)
{
g_autofree gchar *path = NULL;
g_assert (IDE_IS_RUNTIME (self));
g_assert (G_IS_FILE (file));
-
- /* We only need to translate when running as flatpak */
- if (!ide_is_flatpak ())
- return NULL;
+ g_assert (ide_is_flatpak ());
/* Only deal with native files */
if (!g_file_is_native (file) || NULL == (path = g_file_get_path (file)))
@@ -449,11 +275,13 @@ ide_runtime_class_init (IdeRuntimeClass *klass)
i_object_class->repr = ide_runtime_repr;
- klass->create_launcher = ide_runtime_real_create_launcher;
- klass->create_runner = ide_runtime_real_create_runner;
klass->contains_program_in_path = ide_runtime_real_contains_program_in_path;
klass->prepare_configuration = ide_runtime_real_prepare_configuration;
- klass->translate_file = ide_runtime_real_translate_file;
+
+ if (ide_is_flatpak ())
+ klass->translate_file = ide_runtime_flatpak_translate_file;
+ else
+ klass->translate_file = ide_runtime_null_translate_file;
properties [PROP_ID] =
g_param_spec_string ("id",
@@ -654,29 +482,6 @@ ide_runtime_new (const gchar *id,
NULL);
}
-/**
- * ide_runtime_create_launcher:
- *
- * Creates a launcher for the runtime.
- *
- * This can be used to execute a command within a runtime.
- *
- * It is important that this function can be run from a thread without
- * side effects.
- *
- * Returns: (transfer full): An #IdeSubprocessLauncher or %NULL upon failure.
- *
- * Since: 3.32
- */
-IdeSubprocessLauncher *
-ide_runtime_create_launcher (IdeRuntime *self,
- GError **error)
-{
- g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
-
- return IDE_RUNTIME_GET_CLASS (self)->create_launcher (self, error);
-}
-
void
ide_runtime_prepare_configuration (IdeRuntime *self,
IdeConfig *configuration)
@@ -687,33 +492,6 @@ ide_runtime_prepare_configuration (IdeRuntime *self,
IDE_RUNTIME_GET_CLASS (self)->prepare_configuration (self, configuration);
}
-/**
- * ide_runtime_create_runner:
- * @self: An #IdeRuntime
- * @build_target: (nullable): An #IdeBuildTarget or %NULL
- *
- * Creates a new runner that can be used to execute the build target within
- * the runtime. This should be used to implement such features as "run target"
- * or "run unit test" inside the target runtime.
- *
- * If @build_target is %NULL, the runtime should create a runner that allows
- * the caller to specify the binary using the #IdeRunner API.
- *
- * Returns: (transfer full) (nullable): An #IdeRunner if successful, otherwise
- * %NULL and @error is set.
- *
- * Since: 3.32
- */
-IdeRunner *
-ide_runtime_create_runner (IdeRuntime *self,
- IdeBuildTarget *build_target)
-{
- g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
- g_return_val_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target), NULL);
-
- return IDE_RUNTIME_GET_CLASS (self)->create_runner (self, build_target);
-}
-
GQuark
ide_runtime_error_quark (void)
{
@@ -734,8 +512,6 @@ ide_runtime_error_quark (void)
* be accessed from the host system.
*
* Returns: (transfer full) (not nullable): a #GFile.
- *
- * Since: 3.32
*/
GFile *
ide_runtime_translate_file (IdeRuntime *self,
@@ -764,8 +540,6 @@ ide_runtime_translate_file (IdeRuntime *self,
*
* Returns: (transfer full) (array zero-terminated=1): A newly allocated
* string containing the include dirs.
- *
- * Since: 3.32
*/
gchar **
ide_runtime_get_system_include_dirs (IdeRuntime *self)
@@ -791,8 +565,6 @@ ide_runtime_get_system_include_dirs (IdeRuntime *self)
*
* Returns: (transfer full) (not nullable): the architecture triplet the runtime
* will build for.
- *
- * Since: 3.32
*/
IdeTriplet *
ide_runtime_get_triplet (IdeRuntime *self)
@@ -824,8 +596,6 @@ ide_runtime_get_triplet (IdeRuntime *self)
*
* Returns: (transfer full) (not nullable): the name of the architecture
* the runtime will build for.
- *
- * Since: 3.32
*/
gchar *
ide_runtime_get_arch (IdeRuntime *self)
@@ -849,8 +619,6 @@ ide_runtime_get_arch (IdeRuntime *self)
* Informs wether a toolchain is supported by this.
*
* Returns: %TRUE if the toolchain is supported
- *
- * Since: 3.32
*/
gboolean
ide_runtime_supports_toolchain (IdeRuntime *self,
@@ -870,3 +638,100 @@ ide_runtime_supports_toolchain (IdeRuntime *self,
return TRUE;
}
+
+/**
+ * ide_runtime_prepare_to_run:
+ * @self: a #IdeRuntime
+ * @pipeline: (nullable): an #IdePipeline or %NULL for the current
+ * @run_context: an #IdeRunContext
+ *
+ * Prepares a run context to run an application.
+ *
+ * The virtual function implementation should add to the run context anything
+ * necessary to be able to run within the runtime.
+ *
+ * That might include pushing a new layer so that the command will run within
+ * a subcommand such as "flatpak", "jhbuild", or "podman".
+ *
+ * This is meant to be able to run applications, so additional work is expected
+ * of runtimes to ensure access to things like graphical displays.
+ */
+void
+ide_runtime_prepare_to_run (IdeRuntime *self,
+ IdePipeline *pipeline,
+ IdeRunContext *run_context)
+{
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_RUNTIME (self));
+ g_return_if_fail (!pipeline || IDE_IS_PIPELINE (pipeline));
+ g_return_if_fail (IDE_IS_RUN_CONTEXT (run_context));
+
+ if (IDE_RUNTIME_GET_CLASS (self)->prepare_to_run == NULL)
+ IDE_EXIT;
+
+ if (pipeline == NULL)
+ {
+ IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
+ IdeBuildManager *build_manager = ide_build_manager_from_context (context);
+
+ pipeline = ide_build_manager_get_pipeline (build_manager);
+ }
+
+ g_return_if_fail (IDE_IS_PIPELINE (pipeline));
+ g_return_if_fail (ide_pipeline_get_runtime (pipeline) == self);
+
+ IDE_RUNTIME_GET_CLASS (self)->prepare_to_run (self, pipeline, run_context);
+
+ /* Give the run_context access to some environment */
+ ide_run_context_add_minimal_environment (run_context);
+
+ IDE_EXIT;
+}
+
+/**
+ * ide_runtime_prepare_to_build:
+ * @self: a #IdeRuntime
+ * @pipeline: (nullable): an #IdePipeline or %NULL for the current
+ * @run_context: an #IdeRunContext
+ *
+ * Prepares a run context for running a build command.
+ *
+ * The virtual function implementation should add to the run context anything
+ * necessary to be able to run within the runtime.
+ *
+ * That might include pushing a new layer so that the command will run within
+ * a subcommand such as "flatpak", "jhbuild", or "podman".
+ *
+ * This is meant to be able to run a build command, so it may not require
+ * access to some features like network or graphical displays.
+ */
+void
+ide_runtime_prepare_to_build (IdeRuntime *self,
+ IdePipeline *pipeline,
+ IdeRunContext *run_context)
+{
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_RUNTIME (self));
+ g_return_if_fail (!pipeline || IDE_IS_PIPELINE (pipeline));
+ g_return_if_fail (IDE_IS_RUN_CONTEXT (run_context));
+
+ if (IDE_RUNTIME_GET_CLASS (self)->prepare_to_build == NULL)
+ IDE_EXIT;
+
+ if (pipeline == NULL)
+ {
+ IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
+ IdeBuildManager *build_manager = ide_build_manager_from_context (context);
+
+ pipeline = ide_build_manager_get_pipeline (build_manager);
+ }
+
+ g_return_if_fail (IDE_IS_PIPELINE (pipeline));
+ g_return_if_fail (ide_pipeline_get_runtime (pipeline) == self);
+
+ IDE_RUNTIME_GET_CLASS (self)->prepare_to_build (self, pipeline, run_context);
+
+ IDE_EXIT;
+}
diff --git a/src/libide/foundry/ide-runtime.h b/src/libide/foundry/ide-runtime.h
index 65bf2e238..c353bef6e 100644
--- a/src/libide/foundry/ide-runtime.h
+++ b/src/libide/foundry/ide-runtime.h
@@ -42,7 +42,7 @@ typedef enum
#define IDE_TYPE_RUNTIME (ide_runtime_get_type())
#define IDE_RUNTIME_ERROR (ide_runtime_error_quark())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (IdeRuntime, ide_runtime, IDE, RUNTIME, IdeObject)
struct _IdeRuntimeClass
@@ -52,12 +52,14 @@ struct _IdeRuntimeClass
gboolean (*contains_program_in_path) (IdeRuntime *self,
const gchar *program,
GCancellable *cancellable);
- IdeSubprocessLauncher *(*create_launcher) (IdeRuntime *self,
- GError **error);
void (*prepare_configuration) (IdeRuntime *self,
- IdeConfig *configuration);
- IdeRunner *(*create_runner) (IdeRuntime *self,
- IdeBuildTarget *build_target);
+ IdeConfig *config);
+ void (*prepare_to_run) (IdeRuntime *self,
+ IdePipeline *pipeline,
+ IdeRunContext *run_context);
+ void (*prepare_to_build) (IdeRuntime *self,
+ IdePipeline *pipeline,
+ IdeRunContext *run_context);
GFile *(*translate_file) (IdeRuntime *self,
GFile *file);
gchar **(*get_system_include_dirs) (IdeRuntime *self);
@@ -69,59 +71,61 @@ struct _IdeRuntimeClass
gpointer _reserved[12];
};
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
GQuark ide_runtime_error_quark (void) G_GNUC_CONST;
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_runtime_contains_program_in_path (IdeRuntime *self,
const gchar *program,
GCancellable *cancellable);
-IDE_AVAILABLE_IN_3_32
-IdeSubprocessLauncher *ide_runtime_create_launcher (IdeRuntime *self,
- GError **error);
-IDE_AVAILABLE_IN_3_32
-IdeRunner *ide_runtime_create_runner (IdeRuntime *self,
- IdeBuildTarget *build_target);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
+void ide_runtime_prepare_to_run (IdeRuntime *self,
+ IdePipeline *pipeline,
+ IdeRunContext *run_context);
+IDE_AVAILABLE_IN_ALL
+void ide_runtime_prepare_to_build (IdeRuntime *self,
+ IdePipeline *pipeline,
+ IdeRunContext *run_context);
+IDE_AVAILABLE_IN_ALL
void ide_runtime_prepare_configuration (IdeRuntime *self,
IdeConfig *configuration);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeRuntime *ide_runtime_new (const gchar *id,
const gchar *title);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
const gchar *ide_runtime_get_id (IdeRuntime *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_runtime_set_id (IdeRuntime *self,
const gchar *id);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
const gchar *ide_runtime_get_short_id (IdeRuntime *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_runtime_set_short_id (IdeRuntime *self,
const gchar *short_id);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
const gchar *ide_runtime_get_category (IdeRuntime *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_runtime_set_category (IdeRuntime *self,
const gchar *category);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
const gchar *ide_runtime_get_display_name (IdeRuntime *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_runtime_set_display_name (IdeRuntime *self,
const gchar *display_name);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
const gchar *ide_runtime_get_name (IdeRuntime *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_runtime_set_name (IdeRuntime *self,
const gchar *name);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
GFile *ide_runtime_translate_file (IdeRuntime *self,
GFile *file);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gchar **ide_runtime_get_system_include_dirs (IdeRuntime *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gchar *ide_runtime_get_arch (IdeRuntime *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeTriplet *ide_runtime_get_triplet (IdeRuntime *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_runtime_supports_toolchain (IdeRuntime *self,
IdeToolchain *toolchain);
diff --git a/src/libide/foundry/ide-test-manager.c b/src/libide/foundry/ide-test-manager.c
index efc377d27..9227339b8 100644
--- a/src/libide/foundry/ide-test-manager.c
+++ b/src/libide/foundry/ide-test-manager.c
@@ -1,6 +1,6 @@
/* ide-test-manager.c
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2017-2022 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
@@ -22,17 +22,20 @@
#include "config.h"
-#include <dazzle.h>
+#include <libpeas/peas.h>
+
+#include <libide-core.h>
#include <libide-io.h>
#include <libide-threading.h>
-#include <libpeas/peas.h>
#include "ide-build-manager.h"
-#include "ide-pipeline.h"
#include "ide-foundry-compat.h"
+#include "ide-pipeline.h"
+#include "ide-run-command.h"
+#include "ide-run-commands.h"
+#include "ide-run-manager.h"
+#include "ide-test.h"
#include "ide-test-manager.h"
-#include "ide-test-private.h"
-#include "ide-test-provider.h"
#define MAX_UNIT_TESTS 4
@@ -47,483 +50,201 @@
*
* You can access the test manager using ide_context_get_text_manager()
* using the #IdeContext for the loaded project.
- *
- * Since: 3.32
*/
struct _IdeTestManager
{
- IdeObject parent_instance;
-
- PeasExtensionSet *providers;
- GPtrArray *tests_by_provider;
- GtkTreeStore *tests_store;
- GCancellable *cancellable;
- VtePty *pty;
- gint child_pty;
- gint n_active;
+ IdeObject parent_instance;
+ GtkFilterListModel *filtered;
+ IdeCachedListModel *tests;
+ VtePty *pty;
};
typedef struct
{
- IdeTestProvider *provider;
- GPtrArray *tests;
-} TestsByProvider;
+ IdePipeline *pipeline;
+ GPtrArray *tests;
+ VtePty *pty;
+ guint n_active;
+} RunAll;
+
+static void ide_test_manager_actions_test (IdeTestManager *self,
+ GVariant *param);
+static void ide_test_manager_actions_test_all (IdeTestManager *self,
+ GVariant *param);
+
+IDE_DEFINE_ACTION_GROUP (IdeTestManager, ide_test_manager, {
+ { "test", ide_test_manager_actions_test, "s" },
+ { "test-all", ide_test_manager_actions_test_all },
+})
-typedef struct
-{
- GQueue queue;
- guint n_active;
-} RunAllTaskData;
+G_DEFINE_FINAL_TYPE_WITH_CODE (IdeTestManager, ide_test_manager, IDE_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
ide_test_manager_init_action_group))
enum {
PROP_0,
- PROP_LOADING,
+ PROP_MODEL,
N_PROPS
};
-static void initable_iface_init (GInitableIface *iface);
-static void ide_test_manager_actions_run_all (IdeTestManager *self,
- GVariant *param);
-static void ide_test_manager_actions_reload (IdeTestManager *self,
- GVariant *param);
-static void ide_test_manager_actions_cancel (IdeTestManager *self,
- GVariant *param);
-
-DZL_DEFINE_ACTION_GROUP (IdeTestManager, ide_test_manager, {
- { "cancel", ide_test_manager_actions_cancel },
- { "run-all", ide_test_manager_actions_run_all },
- { "reload-tests", ide_test_manager_actions_reload },
-})
-
-G_DEFINE_FINAL_TYPE_WITH_CODE (IdeTestManager, ide_test_manager, IDE_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
- G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
- ide_test_manager_init_action_group))
-
-static GParamSpec *properties [N_PROPS];
-
-static void
-tests_by_provider_free (gpointer data)
-{
- TestsByProvider *info = data;
-
- g_clear_pointer (&info->tests, g_ptr_array_unref);
- g_clear_object (&info->provider);
- g_slice_free (TestsByProvider, info);
-}
-
-static void
-ide_test_manager_destroy (IdeObject *object)
-{
- IdeTestManager *self = (IdeTestManager *)object;
-
- if (self->child_pty != -1)
- {
- close (self->child_pty);
- self->child_pty = -1;
- }
-
- if (self->tests_store != NULL)
- {
- gtk_tree_store_clear (self->tests_store);
- g_clear_object (&self->tests_store);
- }
-
- g_cancellable_cancel (self->cancellable);
- g_clear_object (&self->cancellable);
-
- g_clear_object (&self->providers);
- g_clear_pointer (&self->tests_by_provider, g_ptr_array_unref);
-
- g_clear_object (&self->pty);
-
- IDE_OBJECT_CLASS (ide_test_manager_parent_class)->destroy (object);
-}
-
-static void
-ide_test_manager_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- IdeTestManager *self = IDE_TEST_MANAGER (object);
-
- switch (prop_id)
- {
- case PROP_LOADING:
- g_value_set_boolean (value, ide_test_manager_get_loading (self));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
+static GParamSpec *properties[N_PROPS];
-static void
-ide_test_manager_class_init (IdeTestManagerClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
-
- object_class->get_property = ide_test_manager_get_property;
-
- i_object_class->destroy = ide_test_manager_destroy;
-
- /**
- * IdeTestManager:loading:
- *
- * The "loading" property denotes if a test provider is busy loading
- * tests in the background.
- *
- * Since: 3.32
- */
- properties [PROP_LOADING] =
- g_param_spec_boolean ("loading",
- "Loading",
- "If a test provider is loading tests",
- FALSE,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_properties (object_class, N_PROPS, properties);
-}
-
-static void
-ide_test_manager_init (IdeTestManager *self)
-{
- self->child_pty = -1;
- self->cancellable = g_cancellable_new ();
- self->tests_by_provider = g_ptr_array_new_with_free_func (tests_by_provider_free);
- self->tests_store = gtk_tree_store_new (2, G_TYPE_STRING, IDE_TYPE_TEST);
-
- ide_test_manager_set_action_enabled (self, "cancel", FALSE);
-}
-
-static void
-ide_test_manager_locate_group (IdeTestManager *self,
- GtkTreeIter *iter,
- const gchar *group)
-{
- g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (iter != NULL);
-
- if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->tests_store), iter))
- {
- do
- {
- g_autofree gchar *row_group = NULL;
-
- gtk_tree_model_get (GTK_TREE_MODEL (self->tests_store), iter,
- IDE_TEST_COLUMN_GROUP, &row_group,
- -1);
-
- if (ide_str_equal0 (row_group, group))
- return;
- }
- while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->tests_store), iter));
- }
-
- /* TODO: Sort groups by name? */
-
- gtk_tree_store_append (self->tests_store, iter, NULL);
- gtk_tree_store_set (self->tests_store, iter,
- IDE_TEST_COLUMN_GROUP, group,
- -1);
-}
-
-static void
-ide_test_manager_test_notify_status (IdeTestManager *self,
- GParamSpec *pspec,
- IdeTest *test)
+static gboolean
+filter_tests_func (gpointer item,
+ gpointer user_data)
{
- const gchar *group;
- GtkTreeIter parent;
- GtkTreeIter iter;
-
- g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (IDE_IS_TEST (test));
-
- group = ide_test_get_group (test);
+ IdeRunCommand *run_command = item;
- ide_test_manager_locate_group (self, &parent, group);
-
- if (gtk_tree_model_iter_children (GTK_TREE_MODEL (self->tests_store), &iter, &parent))
- {
- do
- {
- g_autoptr(IdeTest) row_test = NULL;
-
- gtk_tree_model_get (GTK_TREE_MODEL (self->tests_store), &iter,
- IDE_TEST_COLUMN_TEST, &row_test,
- -1);
-
- if (row_test == test)
- {
- GtkTreePath *path;
-
- path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->tests_store), &iter);
- gtk_tree_model_row_changed (GTK_TREE_MODEL (self->tests_store), path, &iter);
- gtk_tree_path_free (path);
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMAND (run_command));
- break;
- }
- }
- while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->tests_store), &iter));
- }
+ return ide_run_command_get_kind (run_command) == IDE_RUN_COMMAND_KIND_TEST;
}
static void
-ide_test_manager_add_test (IdeTestManager *self,
- const TestsByProvider *info,
- guint position,
- IdeTest *test)
+run_all_free (RunAll *state)
{
- const gchar *group;
- GtkTreeIter iter;
- GtkTreeIter parent;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (info != NULL);
- g_assert (IDE_IS_TEST (test));
-
- g_ptr_array_insert (info->tests, position, g_object_ref (test));
-
- group = ide_test_get_group (test);
+ g_assert (state != NULL);
+ g_assert (state->n_active == 0);
- ide_test_manager_locate_group (self, &parent, group);
- gtk_tree_store_append (self->tests_store, &iter, &parent);
- gtk_tree_store_set (self->tests_store, &iter,
- IDE_TEST_COLUMN_GROUP, NULL,
- IDE_TEST_COLUMN_TEST, test,
- -1);
-
- g_signal_connect_object (test,
- "notify::status",
- G_CALLBACK (ide_test_manager_test_notify_status),
- self,
- G_CONNECT_SWAPPED);
-
- IDE_EXIT;
+ g_clear_pointer (&state->tests, g_ptr_array_unref);
+ g_clear_object (&state->pipeline);
+ g_clear_object (&state->pty);
+ g_slice_free (RunAll, state);
}
static void
-ide_test_manager_remove_test (IdeTestManager *self,
- const TestsByProvider *info,
- IdeTest *test)
+ide_test_manager_actions_test (IdeTestManager *self,
+ GVariant *param)
{
- const gchar *group;
- GtkTreeIter iter;
- GtkTreeIter parent;
+ GListModel *tests;
+ const char *test_id;
+ guint n_items;
IDE_ENTRY;
+ g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (info != NULL);
- g_assert (IDE_IS_TEST (test));
-
- group = ide_test_get_group (test);
+ g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
- ide_test_manager_locate_group (self, &parent, group);
+ test_id = g_variant_get_string (param, NULL);
+ tests = ide_test_manager_list_tests (self);
+ n_items = g_list_model_get_n_items (tests);
- if (gtk_tree_model_iter_children (GTK_TREE_MODEL (self->tests_store), &iter, &parent))
+ for (guint i = 0; i < n_items; i++)
{
- do
+ g_autoptr(IdeTest) test = g_list_model_get_item (tests, i);
+
+ if (ide_str_equal0 (test_id, ide_test_get_id (test)))
{
- g_autoptr(IdeTest) row = NULL;
-
- gtk_tree_model_get (GTK_TREE_MODEL (self->tests_store), &iter,
- IDE_TEST_COLUMN_TEST, &row,
- -1);
-
- if (row == test)
- {
- g_signal_handlers_disconnect_by_func (test,
- G_CALLBACK (ide_test_manager_test_notify_status),
- self);
- gtk_tree_store_remove (self->tests_store, &iter);
- break;
- }
+ ide_test_manager_run_async (self, test, NULL, NULL, NULL);
+ IDE_EXIT;
}
- while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->tests_store), &iter));
}
- g_ptr_array_remove (info->tests, test);
-
IDE_EXIT;
}
static void
-ide_test_manager_provider_items_changed (IdeTestManager *self,
- guint position,
- guint removed,
- guint added,
- IdeTestProvider *provider)
+ide_test_manager_actions_test_all (IdeTestManager *self,
+ GVariant *param)
{
IDE_ENTRY;
+ g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (IDE_IS_TEST_PROVIDER (provider));
-
- for (guint i = 0; i < self->tests_by_provider->len; i++)
- {
- const TestsByProvider *info = g_ptr_array_index (self->tests_by_provider, i);
+ g_assert (param == NULL);
- if (info->provider == provider)
- {
- /* Remove tests from cache that were deleted */
- for (guint j = 0; j < removed; j++)
- {
- IdeTest *test = g_ptr_array_index (info->tests, position);
-
- g_assert (IDE_IS_TEST (test));
- ide_test_manager_remove_test (self, info, test);
- }
-
- /* Add tests to cache that were added */
- for (guint j = 0; j < added; j++)
- {
- g_autoptr(IdeTest) test = NULL;
-
- test = g_list_model_get_item (G_LIST_MODEL (provider), position + j);
- g_assert (IDE_IS_TEST (test));
- ide_test_manager_add_test (self, info, position + j, test);
- }
- }
- }
+ ide_test_manager_run_all_async (self, NULL, NULL, NULL);
IDE_EXIT;
}
-static void
-ide_test_manager_provider_notify_loading (IdeTestManager *self,
- GParamSpec *pspec,
- IdeTestProvider *provider)
+static gpointer
+map_run_command_to_test (gpointer item,
+ gpointer user_data)
{
- g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (IDE_IS_TEST_PROVIDER (provider));
+ g_autoptr(IdeRunCommand) run_command = item;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LOADING]);
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMAND (run_command));
+
+ return ide_test_new (run_command);
}
static void
-ide_test_manager_provider_added (PeasExtensionSet *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+ide_test_manager_dispose (GObject *object)
{
- IdeTestManager *self = user_data;
- IdeTestProvider *provider = (IdeTestProvider *)exten;
- TestsByProvider *tests;
- guint len;
-
- IDE_ENTRY;
-
- g_assert (PEAS_IS_EXTENSION_SET (set));
- g_assert (plugin_info != NULL);
- g_assert (IDE_IS_TEST_PROVIDER (provider));
- g_assert (G_IS_LIST_MODEL (provider));
- g_assert (IDE_IS_TEST_MANAGER (self));
-
- tests = g_slice_new0 (TestsByProvider);
- tests->provider = g_object_ref (provider);
- tests->tests = g_ptr_array_new_with_free_func (g_object_unref);
- g_ptr_array_add (self->tests_by_provider, tests);
-
- g_signal_connect_swapped (provider,
- "items-changed",
- G_CALLBACK (ide_test_manager_provider_items_changed),
- self);
- g_signal_connect_swapped (provider,
- "notify::loading",
- G_CALLBACK (ide_test_manager_provider_notify_loading),
- self);
-
- len = g_list_model_get_n_items (G_LIST_MODEL (provider));
- ide_test_manager_provider_items_changed (self, 0, 0, len, provider);
+ IdeTestManager *self = (IdeTestManager *)object;
- ide_object_append (IDE_OBJECT (self), IDE_OBJECT (provider));
+ g_clear_object (&self->pty);
+ g_clear_object (&self->filtered);
+ g_clear_object (&self->tests);
- IDE_EXIT;
+ G_OBJECT_CLASS (ide_test_manager_parent_class)->dispose (object);
}
static void
-ide_test_manager_provider_removed (PeasExtensionSet *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+ide_test_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- IdeTestManager *self = user_data;
- IdeTestProvider *provider = (IdeTestProvider *)exten;
-
- IDE_ENTRY;
-
- g_assert (PEAS_IS_EXTENSION_SET (set));
- g_assert (plugin_info != NULL);
- g_assert (IDE_IS_TEST_PROVIDER (provider));
- g_assert (IDE_IS_TEST_MANAGER (self));
+ IdeTestManager *self = IDE_TEST_MANAGER (object);
- for (guint i = 0; i < self->tests_by_provider->len; i++)
+ switch (prop_id)
{
- const TestsByProvider *info = g_ptr_array_index (self->tests_by_provider, i);
+ case PROP_MODEL:
+ g_value_set_object (value, ide_test_manager_list_tests (self));
+ break;
- if (info->provider == provider)
- {
- g_ptr_array_remove_index (self->tests_by_provider, i);
- break;
- }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
-
- g_signal_handlers_disconnect_by_func (provider,
- G_CALLBACK (ide_test_manager_provider_items_changed),
- self);
- g_signal_handlers_disconnect_by_func (provider,
- G_CALLBACK (ide_test_manager_provider_notify_loading),
- self);
-
- ide_object_destroy (IDE_OBJECT (provider));
-
- IDE_EXIT;
}
-static gboolean
-ide_test_manager_initiable_init (GInitable *initable,
- GCancellable *cancellable,
- GError **error)
+static void
+ide_test_manager_class_init (IdeTestManagerClass *klass)
{
- IdeTestManager *self = (IdeTestManager *)initable;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- self->providers = peas_extension_set_new (peas_engine_get_default (),
- IDE_TYPE_TEST_PROVIDER,
- NULL);
-
- g_signal_connect (self->providers,
- "extension-added",
- G_CALLBACK (ide_test_manager_provider_added),
- self);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
- g_signal_connect (self->providers,
- "extension-removed",
- G_CALLBACK (ide_test_manager_provider_removed),
- self);
+ object_class->dispose = ide_test_manager_dispose;
+ object_class->get_property = ide_test_manager_get_property;
- peas_extension_set_foreach (self->providers,
- ide_test_manager_provider_added,
- self);
+ /**
+ * IdeTestManager:model:
+ *
+ * The "model" property contains a #GListModel of #IdeTest.
+ *
+ * Fetching this property will not cause the #GListModel to be
+ * populated. That is only done by calling ide_test_manager_list_tests().
+ *
+ * This may be a more convenient way to get access to the model when you
+ * do not want the tests to autopopulate just to be bound to UI elements.
+ */
+ properties [PROP_MODEL] =
+ g_param_spec_object ("model", NULL, NULL,
+ G_TYPE_LIST_MODEL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- IDE_RETURN (TRUE);
+ g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
-initable_iface_init (GInitableIface *iface)
+ide_test_manager_init (IdeTestManager *self)
{
- iface->init = ide_test_manager_initiable_init;
+ GtkCustomFilter *filter;
+ GtkMapListModel *map;
+
+ self->pty = vte_pty_new_sync (VTE_PTY_DEFAULT, NULL, NULL);
+ vte_pty_set_utf8 (self->pty, TRUE, NULL);
+
+ filter = gtk_custom_filter_new (filter_tests_func, NULL, NULL);
+ self->filtered = gtk_filter_list_model_new (NULL, GTK_FILTER (filter));
+ map = gtk_map_list_model_new (g_object_ref (G_LIST_MODEL (self->filtered)),
+ map_run_command_to_test,
+ NULL, NULL);
+ self->tests = ide_cached_list_model_new (G_LIST_MODEL (map));
}
static void
@@ -531,43 +252,49 @@ ide_test_manager_run_all_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
- IdeTestManager *self = (IdeTestManager *)object;
- g_autoptr(GTask) task = user_data;
+ IdeTest *test = (IdeTest *)object;
+ g_autoptr(IdeTask) task = user_data;
g_autoptr(GError) error = NULL;
- g_autoptr(IdeTest) test = NULL;
- RunAllTaskData *task_data;
GCancellable *cancellable;
+ RunAll *state;
IDE_ENTRY;
- g_assert (IDE_IS_TEST_MANAGER (self));
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_TEST (test));
g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (G_IS_TASK (task));
+ g_assert (IDE_IS_TASK (task));
- cancellable = g_task_get_cancellable (task);
- task_data = g_task_get_task_data (task);
- g_assert (task_data != NULL);
- g_assert (task_data->n_active > 0);
+ cancellable = ide_task_get_cancellable (task);
+ state = ide_task_get_task_data (task);
- if (!ide_test_manager_run_finish (self, result, &error))
- g_message ("%s", error->message);
+ g_assert (state != NULL);
+ g_assert (state->n_active > 0);
+ g_assert (!state->pty || VTE_IS_PTY (state->pty));
+ g_assert (state->tests != NULL);
- test = g_queue_pop_head (&task_data->queue);
+ if (!ide_test_run_finish (test, result, &error))
+ g_message ("%s", error->message);
- if (test != NULL)
+ if (state->tests->len > 0 &&
+ !g_cancellable_is_cancelled (cancellable))
{
- task_data->n_active++;
- ide_test_manager_run_async (self,
- test,
- cancellable,
- ide_test_manager_run_all_cb,
- g_object_ref (task));
+ g_autoptr(IdeTest) next_test = g_ptr_array_steal_index (state->tests, state->tests->len-1);
+
+ state->n_active++;
+
+ ide_test_run_async (next_test,
+ state->pipeline,
+ state->pty,
+ cancellable,
+ ide_test_manager_run_all_cb,
+ g_object_ref (task));
}
- task_data->n_active--;
+ state->n_active--;
- if (task_data->n_active == 0)
- g_task_return_boolean (task, TRUE);
+ if (state->n_active == 0)
+ ide_task_return_boolean (task, TRUE);
IDE_EXIT;
}
@@ -586,8 +313,6 @@ ide_test_manager_run_all_cb (GObject *object,
*
* Note that the individual test result information will be attached
* to the specific #IdeTest instances.
- *
- * Since: 3.32
*/
void
ide_test_manager_run_all_async (IdeTestManager *self,
@@ -595,55 +320,67 @@ ide_test_manager_run_all_async (IdeTestManager *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- g_autoptr(GTask) task = NULL;
- RunAllTaskData *task_data;
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GPtrArray) ar = NULL;
+ IdeBuildManager *build_manager;
+ IdePipeline *pipeline;
+ GListModel *tests;
+ IdeContext *context;
+ RunAll *state;
+ guint n_items;
IDE_ENTRY;
g_return_if_fail (IDE_IS_TEST_MANAGER (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- task = g_task_new (self, cancellable, callback, user_data);
- g_task_set_priority (task, G_PRIORITY_LOW);
- g_task_set_source_tag (task, ide_test_manager_run_all_async);
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_test_manager_run_all_async);
- task_data = g_new0 (RunAllTaskData, 1);
- g_task_set_task_data (task, task_data, g_free);
+ context = ide_object_get_context (IDE_OBJECT (self));
+ build_manager = ide_build_manager_from_context (context);
+ pipeline = ide_build_manager_get_pipeline (build_manager);
- for (guint i = 0; i < self->tests_by_provider->len; i++)
+ if (pipeline == NULL)
{
- TestsByProvider *info = g_ptr_array_index (self->tests_by_provider, i);
-
- for (guint j = 0; j < info->tests->len; j++)
- {
- IdeTest *test = g_ptr_array_index (info->tests, j);
-
- g_queue_push_tail (&task_data->queue, g_object_ref (test));
- }
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_INITIALIZED,
+ "Cannot run test until pipeline is ready");
+ IDE_EXIT;
}
- task_data->n_active = MIN (MAX_UNIT_TESTS, task_data->queue.length);
+ tests = ide_test_manager_list_tests (self);
+ n_items = g_list_model_get_n_items (tests);
- if (task_data->n_active == 0)
- {
- g_task_return_boolean (task, TRUE);
- IDE_EXIT;
- }
+ ar = g_ptr_array_new_with_free_func (g_object_unref);
+ for (guint i = n_items; i > 0; i--)
+ g_ptr_array_add (ar, g_list_model_get_item (tests, i-1));
+
+ state = g_slice_new0 (RunAll);
+ state->tests = g_ptr_array_ref (ar);
+ state->pipeline = g_object_ref (pipeline);
+ state->pty = g_object_ref (self->pty);
+ state->n_active = 0;
+ ide_task_set_task_data (task, state, run_all_free);
- for (guint i = 0; i < MAX_UNIT_TESTS; i++)
+ for (guint i = 0; i < MAX_UNIT_TESTS && ar->len > 0; i++)
{
- g_autoptr(IdeTest) test = g_queue_pop_head (&task_data->queue);
+ g_autoptr(IdeTest) test = g_ptr_array_steal_index (state->tests, ar->len-1);
- if (test == NULL)
- break;
+ state->n_active++;
- ide_test_manager_run_async (self,
- test,
- cancellable,
- ide_test_manager_run_all_cb,
- g_object_ref (task));
+ ide_test_run_async (test,
+ state->pipeline,
+ state->pty,
+ cancellable,
+ ide_test_manager_run_all_cb,
+ g_object_ref (task));
}
+ if (state->n_active == 0)
+ ide_task_return_boolean (task, TRUE);
+
IDE_EXIT;
}
@@ -660,8 +397,6 @@ ide_test_manager_run_all_async (IdeTestManager *self,
* attached to the #IdeTest instances.
*
* Returns: %TRUE if successful; otherwise %FALSE and @error is set.
- *
- * Since: 3.32
*/
gboolean
ide_test_manager_run_all_finish (IdeTestManager *self,
@@ -673,46 +408,33 @@ ide_test_manager_run_all_finish (IdeTestManager *self,
IDE_ENTRY;
g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), FALSE);
- g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
- ret = g_task_propagate_boolean (G_TASK (result), error);
+ ret = ide_task_propagate_boolean (IDE_TASK (result), error);
IDE_RETURN (ret);
}
-static void
-run_task_completed_cb (IdeTestManager *self,
- GParamSpec *pspec,
- IdeTask *task)
-{
- g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (G_IS_TASK (task));
- g_assert (self->n_active > 0);
-
- self->n_active--;
-
- ide_test_manager_set_action_enabled (self, "cancel", self->n_active > 0);
-}
-
static void
ide_test_manager_run_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
- IdeTestProvider *provider = (IdeTestProvider *)object;
- g_autoptr(GTask) task = user_data;
+ IdeTest *test = (IdeTest *)object;
+ g_autoptr(IdeTask) task = user_data;
g_autoptr(GError) error = NULL;
IDE_ENTRY;
- g_assert (IDE_IS_TEST_PROVIDER (provider));
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_TEST (test));
g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (G_IS_TASK (task));
+ g_assert (IDE_IS_TASK (task));
- if (!ide_test_provider_run_finish (provider, result, &error))
- g_task_return_error (task, g_steal_pointer (&error));
+ if (!ide_test_run_finish (test, result, &error))
+ ide_task_return_error (task, g_steal_pointer (&error));
else
- g_task_return_boolean (task, TRUE);
+ ide_task_return_boolean (task, TRUE);
IDE_EXIT;
}
@@ -729,8 +451,6 @@ ide_test_manager_run_cb (GObject *object,
*
* The caller can access the result of the operation from @callback
* by calling ide_test_manager_run_finish() with the provided result.
- *
- * Since: 3.32
*/
void
ide_test_manager_run_async (IdeTestManager *self,
@@ -739,10 +459,9 @@ ide_test_manager_run_async (IdeTestManager *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- g_autoptr(GTask) task = NULL;
- IdePipeline *pipeline;
- IdeTestProvider *provider;
+ g_autoptr(IdeTask) task = NULL;
IdeBuildManager *build_manager;
+ IdePipeline *pipeline;
IdeContext *context;
IDE_ENTRY;
@@ -751,51 +470,25 @@ ide_test_manager_run_async (IdeTestManager *self,
g_return_if_fail (IDE_IS_TEST (test));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- task = g_task_new (self, cancellable, callback, user_data);
- g_task_set_priority (task, G_PRIORITY_LOW);
- g_task_set_source_tag (task, ide_test_manager_run_async);
-
- self->n_active++;
- g_signal_connect_object (task,
- "notify::completed",
- G_CALLBACK (run_task_completed_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_test_manager_set_action_enabled (self, "cancel", TRUE);
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_test_manager_run_async);
context = ide_object_get_context (IDE_OBJECT (self));
build_manager = ide_build_manager_from_context (context);
pipeline = ide_build_manager_get_pipeline (build_manager);
if (pipeline == NULL)
- {
- g_task_return_new_error (task,
+ ide_task_return_new_error (task,
G_IO_ERROR,
- G_IO_ERROR_FAILED,
+ G_IO_ERROR_NOT_INITIALIZED,
"Pipeline is not ready, cannot run test");
- IDE_EXIT;
- }
-
- provider = _ide_test_get_provider (test);
-
- if (self->pty == NULL)
- {
- g_autoptr(GError) error = NULL;
-
- if (!(self->pty = vte_pty_new_sync (VTE_PTY_DEFAULT, cancellable, &error)))
- {
- g_task_return_error (task, g_steal_pointer (&error));
- IDE_EXIT;
- }
- }
-
- ide_test_provider_run_async (provider,
- test,
- pipeline,
- self->pty,
- cancellable,
- ide_test_manager_run_cb,
- g_steal_pointer (&task));
+ else
+ ide_test_run_async (test,
+ pipeline,
+ self->pty,
+ cancellable,
+ ide_test_manager_run_cb,
+ g_steal_pointer (&task));
IDE_EXIT;
}
@@ -814,8 +507,6 @@ ide_test_manager_run_async (IdeTestManager *self,
*
* Returns: %TRUE if the test was executed; otherwise %FALSE
* and @error is set.
- *
- * Since: 3.32
*/
gboolean
ide_test_manager_run_finish (IdeTestManager *self,
@@ -827,258 +518,13 @@ ide_test_manager_run_finish (IdeTestManager *self,
IDE_ENTRY;
g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), FALSE);
- g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
- ret = g_task_propagate_boolean (G_TASK (result), error);
+ ret = ide_task_propagate_boolean (IDE_TASK (result), error);
IDE_RETURN (ret);
}
-static void
-ide_test_manager_actions_run_all (IdeTestManager *self,
- GVariant *param)
-{
- g_assert (IDE_IS_TEST_MANAGER (self));
-
- ide_test_manager_run_all_async (self, NULL, NULL, NULL);
-}
-
-static void
-ide_test_manager_actions_reload (IdeTestManager *self,
- GVariant *param)
-{
- g_assert (IDE_IS_TEST_MANAGER (self));
-
- gtk_tree_store_clear (self->tests_store);
-
- for (guint i = 0; i < self->tests_by_provider->len; i++)
- {
- const TestsByProvider *info = g_ptr_array_index (self->tests_by_provider, i);
-
- ide_test_provider_reload (info->provider);
- }
-}
-
-GtkTreeModel *
-_ide_test_manager_get_model (IdeTestManager *self)
-{
- g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), NULL);
-
- return GTK_TREE_MODEL (self->tests_store);
-}
-
-static void
-ide_test_manager_get_loading_cb (PeasExtensionSet *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
-{
- IdeTestProvider *provider = (IdeTestProvider *)exten;
- gboolean *loading = user_data;
-
- g_assert (PEAS_IS_EXTENSION_SET (set));
- g_assert (plugin_info != NULL);
- g_assert (IDE_IS_TEST_PROVIDER (provider));
- g_assert (loading != NULL);
-
- *loading |= ide_test_provider_get_loading (provider);
-}
-
-gboolean
-ide_test_manager_get_loading (IdeTestManager *self)
-{
- gboolean loading = FALSE;
-
- g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), FALSE);
-
- peas_extension_set_foreach (self->providers,
- ide_test_manager_get_loading_cb,
- &loading);
-
- return loading;
-}
-
-/**
- * ide_test_manager_get_tests:
- * @self: a #IdeTestManager
- * @path: (nullable): the path to the test or %NULL for the root path
- *
- * Locates and returns any #IdeTest that is found as a direct child
- * of @path.
- *
- * Returns: (transfer full) (element-type IdeTest): an array of #IdeTest
- *
- * Since: 3.32
- */
-GPtrArray *
-ide_test_manager_get_tests (IdeTestManager *self,
- const gchar *path)
-{
- GPtrArray *ret;
- GtkTreeIter iter;
-
- g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
- g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), NULL);
-
- ret = g_ptr_array_new ();
-
- if (path == NULL)
- {
- if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->tests_store), &iter))
- goto failure;
- }
- else
- {
- GtkTreeIter parent;
-
- ide_test_manager_locate_group (self, &parent, path);
-
- if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (self->tests_store), &iter, &parent))
- goto failure;
- }
-
- do
- {
- IdeTest *test = NULL;
-
- gtk_tree_model_get (GTK_TREE_MODEL (self->tests_store), &iter,
- IDE_TEST_COLUMN_TEST, &test,
- -1);
- if (test != NULL)
- g_ptr_array_add (ret, g_steal_pointer (&test));
- }
- while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->tests_store), &iter));
-
-failure:
- return g_steal_pointer (&ret);
-}
-
-/**
- * ide_test_manager_get_folders:
- * @self: a #IdeTestManager
- * @path: (nullable): the path to the test or %NULL for the root path
- *
- * Gets the sub-paths of @path that are not individual tests.
- *
- * Returns: (transfer full) (array zero-terminated=1): an array of strings
- * describing available sub-paths to @path.
- *
- * Since: 3.32
- */
-gchar **
-ide_test_manager_get_folders (IdeTestManager *self,
- const gchar *path)
-{
- static const gchar *empty[] = { NULL };
- GPtrArray *ret;
- GtkTreeIter iter;
-
- g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
- g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), NULL);
-
- ret = g_ptr_array_new ();
-
- if (path == NULL)
- {
- if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->tests_store), &iter))
- return g_strdupv ((gchar **)empty);
- }
- else
- {
- GtkTreeIter parent;
-
- ide_test_manager_locate_group (self, &parent, path);
-
- if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (self->tests_store), &iter, &parent))
- return g_strdupv ((gchar **)empty);
- }
-
- do
- {
- gchar *group = NULL;
-
- gtk_tree_model_get (GTK_TREE_MODEL (self->tests_store), &iter,
- IDE_TEST_COLUMN_GROUP, &group,
- -1);
- if (group != NULL)
- g_ptr_array_add (ret, g_steal_pointer (&group));
- }
- while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->tests_store), &iter));
-
- g_ptr_array_add (ret, NULL);
-
- return (gchar **)g_ptr_array_free (ret, FALSE);
-}
-
-static void
-ide_test_manager_ensure_loaded_cb (IdeTestManager *self,
- GParamSpec *pspec,
- IdeTask *task)
-{
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_TEST_MANAGER (self));
- g_assert (IDE_IS_TASK (task));
-
- if (!ide_test_manager_get_loading (self))
- {
- g_signal_handlers_disconnect_by_func (self,
- G_CALLBACK (ide_test_manager_ensure_loaded_cb),
- task);
- ide_task_return_boolean (task, TRUE);
- }
-}
-
-/**
- * ide_test_manager_ensure_loaded_async:
- * @self: a #IdeTestManager
- *
- * Calls @callback after the test manager has loaded tests.
- *
- * If the test manager has already loaded tests, then @callback will
- * be called after returning to the main loop.
- *
- * Since: 3.32
- */
-void
-ide_test_manager_ensure_loaded_async (IdeTestManager *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- g_autoptr(IdeTask) task = NULL;
-
- g_return_if_fail (IDE_IS_TEST_MANAGER (self));
- g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- task = ide_task_new (self, cancellable, callback, user_data);
- ide_task_set_source_tag (task, ide_test_manager_ensure_loaded_async);
-
- if (ide_test_manager_get_loading (self))
- {
- g_signal_connect_data (self,
- "notify::loading",
- G_CALLBACK (ide_test_manager_ensure_loaded_cb),
- g_steal_pointer (&task),
- (GClosureNotify)g_object_unref,
- 0);
- return;
- }
-
- ide_task_return_boolean (task, TRUE);
-}
-
-gboolean
-ide_test_manager_ensure_loaded_finish (IdeTestManager *self,
- GAsyncResult *result,
- GError **error)
-{
- g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
- g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), FALSE);
- g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
-
- return ide_task_propagate_boolean (IDE_TASK (result), error);
-}
-
/**
* ide_test_manager_get_pty:
* @self: a #IdeTestManager
@@ -1086,70 +532,42 @@ ide_test_manager_ensure_loaded_finish (IdeTestManager *self,
* Gets the #VtePty to use for running unit tests.
*
* Returns: (transfer none): a #VtePty
- *
- * Since: 3.32
*/
VtePty *
ide_test_manager_get_pty (IdeTestManager *self)
{
g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), NULL);
- if (self->pty == NULL)
- self->pty = vte_pty_new_sync (VTE_PTY_DEFAULT, NULL, NULL);
-
return self->pty;
}
/**
- * ide_test_manager_open_pty:
+ * ide_test_manager_list_tests:
* @self: a #IdeTestManager
*
- * Gets a FD that maps to the child side of the PTY device.
+ * Gets a #GListModel of #IdeTest.
*
- * Returns: a new FD or -1 on failure
+ * This will return a #GListModel immediately, but that list may not complete
+ * until some time in the future based on how quickly various
+ * #IdeRunCommandProvider return commands.
*
- * Since: 3.34
+ * Returns: (transfer none): an #GListModel of #IdeTest
*/
-gint
-ide_test_manager_open_pty (IdeTestManager *self)
+GListModel *
+ide_test_manager_list_tests (IdeTestManager *self)
{
- g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), -1);
-
- if (self->child_pty == -1)
- {
- VtePty *pty = ide_test_manager_get_pty (self);
- self->child_pty = ide_pty_intercept_create_slave (vte_pty_get_fd (pty), TRUE);
- }
-
- return dup (self->child_pty);
-}
+ IDE_ENTRY;
-/**
- * ide_test_manager_get_cancellable:
- * @self: a #IdeTestManager
- *
- * Gets the cancellable for the test manager which will be cancelled
- * when the cancel action is called.
- *
- * Returns: (transfer none): a #GCancellable
- *
- * Since: 3.34
- */
-GCancellable *
-ide_test_manager_get_cancellable (IdeTestManager *self)
-{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
g_return_val_if_fail (IDE_IS_TEST_MANAGER (self), NULL);
- return self->cancellable;
-}
+ if (gtk_filter_list_model_get_model (self->filtered) == NULL)
+ {
+ IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
+ IdeRunCommands *run_commands = ide_run_commands_from_context (context);
-static void
-ide_test_manager_actions_cancel (IdeTestManager *self,
- GVariant *param)
-{
- g_assert (IDE_IS_TEST_MANAGER (self));
+ gtk_filter_list_model_set_model (self->filtered, G_LIST_MODEL (run_commands));
+ }
- g_cancellable_cancel (self->cancellable);
- g_clear_object (&self->cancellable);
- self->cancellable = g_cancellable_new ();
+ IDE_RETURN (G_LIST_MODEL (self->tests));
}
diff --git a/src/libide/foundry/ide-test-manager.h b/src/libide/foundry/ide-test-manager.h
index edb3e28b5..3e7c60e63 100644
--- a/src/libide/foundry/ide-test-manager.h
+++ b/src/libide/foundry/ide-test-manager.h
@@ -24,61 +24,43 @@
# error "Only <libide-foundry.h> can be included directly."
#endif
-#include <libide-core.h>
#include <vte/vte.h>
+#include <libide-core.h>
+
#include "ide-foundry-types.h"
G_BEGIN_DECLS
#define IDE_TYPE_TEST_MANAGER (ide_test_manager_get_type())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (IdeTestManager, ide_test_manager, IDE, TEST_MANAGER, IdeObject)
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeTestManager *ide_test_manager_from_context (IdeContext *context);
-IDE_AVAILABLE_IN_3_32
-gboolean ide_test_manager_get_loading (IdeTestManager *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
VtePty *ide_test_manager_get_pty (IdeTestManager *self);
-IDE_AVAILABLE_IN_3_34
-gint ide_test_manager_open_pty (IdeTestManager *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
+GListModel *ide_test_manager_list_tests (IdeTestManager *self);
+IDE_AVAILABLE_IN_ALL
void ide_test_manager_run_async (IdeTestManager *self,
IdeTest *test,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_test_manager_run_finish (IdeTestManager *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_test_manager_run_all_async (IdeTestManager *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_test_manager_run_all_finish (IdeTestManager *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
-GPtrArray *ide_test_manager_get_tests (IdeTestManager *self,
- const gchar *path);
-IDE_AVAILABLE_IN_3_32
-gchar **ide_test_manager_get_folders (IdeTestManager *self,
- const gchar *path);
-IDE_AVAILABLE_IN_3_34
-GCancellable *ide_test_manager_get_cancellable (IdeTestManager *self);
-IDE_AVAILABLE_IN_3_32
-void ide_test_manager_ensure_loaded_async (IdeTestManager *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-IDE_AVAILABLE_IN_3_32
-gboolean ide_test_manager_ensure_loaded_finish (IdeTestManager *self,
- GAsyncResult *result,
- GError **error);
G_END_DECLS
diff --git a/src/libide/foundry/ide-test.c b/src/libide/foundry/ide-test.c
index f2980bbe4..55790ccc9 100644
--- a/src/libide/foundry/ide-test.c
+++ b/src/libide/foundry/ide-test.c
@@ -1,6 +1,6 @@
/* ide-test.c
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2017-2022 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
@@ -22,57 +22,60 @@
#include "config.h"
-#include "ide-foundry-enums.h"
+#include <libide-io.h>
+#include <libide-threading.h>
+#include "ide-run-command.h"
+#include "ide-run-context.h"
+#include "ide-foundry-enums.h"
+#include "ide-pipeline.h"
+#include "ide-runtime.h"
#include "ide-test.h"
-#include "ide-test-private.h"
-#include "ide-test-provider.h"
-typedef struct
+struct _IdeTest
{
- /* Unowned references */
- IdeTestProvider *provider;
-
- /* Owned references */
- gchar *display_name;
- gchar *group;
- gchar *id;
-
- IdeTestStatus status;
-} IdeTestPrivate;
-
-G_DEFINE_TYPE_WITH_PRIVATE (IdeTest, ide_test, G_TYPE_OBJECT)
+ GObject parent_instance;
+ IdeRunCommand *run_command;
+ IdeTestStatus status;
+};
enum {
PROP_0,
- PROP_DISPLAY_NAME,
- PROP_GROUP,
+ PROP_ICON_NAME,
PROP_ID,
+ PROP_RUN_COMMAND,
PROP_STATUS,
+ PROP_TITLE,
N_PROPS
};
+G_DEFINE_FINAL_TYPE (IdeTest, ide_test, G_TYPE_OBJECT)
+
static GParamSpec *properties [N_PROPS];
-IdeTest *
-ide_test_new (void)
+static void
+ide_test_set_status (IdeTest *self,
+ IdeTestStatus status)
{
- return g_object_new (IDE_TYPE_TEST, NULL);
+ g_assert (IDE_IS_TEST (self));
+
+ if (status != self->status)
+ {
+ self->status = status;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_STATUS]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON_NAME]);
+ }
}
static void
-ide_test_finalize (GObject *object)
+ide_test_dispose (GObject *object)
{
IdeTest *self = (IdeTest *)object;
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
-
- priv->provider = NULL;
- g_clear_pointer (&priv->group, g_free);
- g_clear_pointer (&priv->id, g_free);
- g_clear_pointer (&priv->display_name, g_free);
+ g_clear_object (&self->run_command);
- G_OBJECT_CLASS (ide_test_parent_class)->finalize (object);
+ G_OBJECT_CLASS (ide_test_parent_class)->dispose (object);
}
static void
@@ -85,22 +88,26 @@ ide_test_get_property (GObject *object,
switch (prop_id)
{
- case PROP_ID:
- g_value_set_string (value, ide_test_get_id (self));
+ case PROP_ICON_NAME:
+ g_value_set_string (value, ide_test_get_icon_name (self));
break;
- case PROP_GROUP:
- g_value_set_string (value, ide_test_get_group (self));
+ case PROP_ID:
+ g_value_set_string (value, ide_test_get_id (self));
break;
- case PROP_DISPLAY_NAME:
- g_value_set_string (value, ide_test_get_display_name (self));
+ case PROP_RUN_COMMAND:
+ g_value_set_object (value, ide_test_get_run_command (self));
break;
case PROP_STATUS:
g_value_set_enum (value, ide_test_get_status (self));
break;
+ case PROP_TITLE:
+ g_value_set_string (value, ide_test_get_title (self));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -116,20 +123,8 @@ ide_test_set_property (GObject *object,
switch (prop_id)
{
- case PROP_GROUP:
- ide_test_set_group (self, g_value_get_string (value));
- break;
-
- case PROP_ID:
- ide_test_set_id (self, g_value_get_string (value));
- break;
-
- case PROP_DISPLAY_NAME:
- ide_test_set_display_name (self, g_value_get_string (value));
- break;
-
- case PROP_STATUS:
- ide_test_set_status (self, g_value_get_enum (value));
+ case PROP_RUN_COMMAND:
+ g_set_object (&self->run_command, g_value_get_object (value));
break;
default:
@@ -142,69 +137,32 @@ ide_test_class_init (IdeTestClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = ide_test_finalize;
+ object_class->dispose = ide_test_dispose;
object_class->get_property = ide_test_get_property;
object_class->set_property = ide_test_set_property;
- /**
- * IdeTest:display_name:
- *
- * The "display-name" property contains the display name of the test as
- * the user is expected to read in UI elements.
- *
- * Since: 3.32
- */
- properties [PROP_DISPLAY_NAME] =
- g_param_spec_string ("display-name",
- "Name",
- "The display_name of the unit test",
- NULL,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- /**
- * IdeTest:id:
- *
- * The "id" property contains the unique identifier of the test.
- *
- * Since: 3.32
- */
+ properties [PROP_ICON_NAME] =
+ g_param_spec_string ("icon-name", NULL, NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
properties [PROP_ID] =
- g_param_spec_string ("id",
- "Id",
- "The unique identifier of the test",
- NULL,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- /**
- * IdeTest:group:
- *
- * The "group" property contains the name of the gruop the test belongs
- * to, if any.
- *
- * Since: 3.32
- */
- properties [PROP_GROUP] =
- g_param_spec_string ("group",
- "Group",
- "The name of the group the test belongs to, if any",
- NULL,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- /**
- * IdeTest::status:
- *
- * The "status" property contains the status of the test, updated by
- * providers when they have run the test.
- *
- * Since: 3.32
- */
+ g_param_spec_string ("id", NULL, NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RUN_COMMAND] =
+ g_param_spec_object ("run-command", NULL, NULL,
+ IDE_TYPE_RUN_COMMAND,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
properties [PROP_STATUS] =
- g_param_spec_enum ("status",
- "Status",
- "The status of the test",
+ g_param_spec_enum ("status", NULL, NULL,
IDE_TYPE_TEST_STATUS,
IDE_TEST_STATUS_NONE,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_TITLE] =
+ g_param_spec_string ("title", NULL, NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
@@ -214,216 +172,195 @@ ide_test_init (IdeTest *self)
{
}
-IdeTestProvider *
-_ide_test_get_provider (IdeTest *self)
+IdeTest *
+ide_test_new (IdeRunCommand *run_command)
{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (run_command), NULL);
+
+ return g_object_new (IDE_TYPE_TEST,
+ "run-command", run_command,
+ NULL);
+}
+const char *
+ide_test_get_id (IdeTest *self)
+{
g_return_val_if_fail (IDE_IS_TEST (self), NULL);
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self->run_command), NULL);
- return priv->provider;
+ return ide_run_command_get_id (self->run_command);
}
-void
-_ide_test_set_provider (IdeTest *self,
- IdeTestProvider *provider)
+IdeTestStatus
+ide_test_get_status (IdeTest *self)
{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_TEST (self));
- g_return_if_fail (!provider || IDE_IS_TEST_PROVIDER (provider));
+ g_return_val_if_fail (IDE_IS_TEST (self), 0);
- priv->provider = provider;
+ return self->status;
}
-/**
- * ide_test_get_display_name:
- * @self: An #IdeTest
- *
- * Gets the "display-name" property of the test.
- *
- * Returns: (nullable): The display_name of the test or %NULL
- *
- * Since: 3.32
- */
-const gchar *
-ide_test_get_display_name (IdeTest *self)
+const char *
+ide_test_get_title (IdeTest *self)
{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
-
g_return_val_if_fail (IDE_IS_TEST (self), NULL);
+ g_return_val_if_fail (IDE_IS_RUN_COMMAND (self->run_command), NULL);
- return priv->display_name;
+ return ide_run_command_get_display_name (self->run_command);
}
-/**
- * ide_test_set_display_name:
- * @self: An #IdeTest
- * @display_name: (nullable): The display_name of the test, or %NULL to unset
- *
- * Sets the "display-name" property of the unit test.
- *
- * Since: 3.32
- */
-void
-ide_test_set_display_name (IdeTest *self,
- const gchar *display_name)
+const char *
+ide_test_get_icon_name (IdeTest *self)
{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_TEST (self));
+ g_return_val_if_fail (IDE_IS_TEST (self), NULL);
- if (g_strcmp0 (display_name, priv->display_name) != 0)
+ switch (self->status)
{
- g_free (priv->display_name);
- priv->display_name = g_strdup (display_name);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISPLAY_NAME]);
+ case IDE_TEST_STATUS_NONE:
+ return "builder-unit-tests-symbolic";
+
+ case IDE_TEST_STATUS_RUNNING:
+ return "builder-unit-tests-running-symbolic";
+
+ case IDE_TEST_STATUS_FAILED:
+ return "builder-unit-tests-fail-symbolic";
+
+ case IDE_TEST_STATUS_SUCCESS:
+ return "builder-unit-tests-pass-symbolic";
+
+ default:
+ g_return_val_if_reached (NULL);
}
}
/**
- * ide_test_get_group:
+ * ide_test_get_run_command:
* @self: a #IdeTest
*
- * Gets the "group" property.
- *
- * The group name is used to group tests together.
- *
- * Returns: (nullable): The group name or %NULL.
+ * Gets the run command for the test.
*
- * Since: 3.32
+ * Returns: (transfer none): an #IdeTest
*/
-const gchar *
-ide_test_get_group (IdeTest *self)
+IdeRunCommand *
+ide_test_get_run_command (IdeTest *self)
{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
-
g_return_val_if_fail (IDE_IS_TEST (self), NULL);
- return priv->group;
+ return self->run_command;
}
-/**
- * ide_test_set_group:
- * @self: a #IdeTest
- * @group: (nullable): the name of the group or %NULL
- *
- * Sets the #IdeTest:group property.
- *
- * The group property is used to group related tests together.
- *
- * Since: 3.32
- */
-void
-ide_test_set_group (IdeTest *self,
- const gchar *group)
+static void
+ide_test_wait_check_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
+ IdeSubprocess *subprocess = (IdeSubprocess *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeTest *self;
- g_return_if_fail (IDE_IS_TEST (self));
+ IDE_ENTRY;
- if (g_strcmp0 (group, priv->group) != 0)
- {
- g_free (priv->group);
- priv->group = g_strdup (group);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_GROUP]);
- }
-}
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SUBPROCESS (subprocess));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
-/**
- * ide_test_get_id:
- * @self: a #IdeTest
- *
- * Gets the #IdeTest:id property.
- *
- * Returns: (nullable): The id of the test, or %NULL if it has not been set.
- *
- * Since: 3.32
- */
-const gchar *
-ide_test_get_id (IdeTest *self)
-{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
+ self = ide_task_get_source_object (task);
- g_return_val_if_fail (IDE_IS_TEST (self), NULL);
+ if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
+ {
+ ide_test_set_status (self, IDE_TEST_STATUS_FAILED);
+ ide_task_return_error (task, g_steal_pointer (&error));
+ }
+ else
+ {
+ ide_test_set_status (self, IDE_TEST_STATUS_SUCCESS);
+ ide_task_return_boolean (task, TRUE);
+ }
- return priv->id;
+ IDE_EXIT;
}
-/**
- * ide_test_set_id:
- * @self: a #IdeTest
- * @id: (nullable): the id of the test or %NULL
- *
- * Sets the #IdeTest:id property.
- *
- * The id property is used to uniquely identify the test.
- *
- * Since: 3.32
- */
void
-ide_test_set_id (IdeTest *self,
- const gchar *id)
+ide_test_run_async (IdeTest *self,
+ IdePipeline *pipeline,
+ VtePty *pty,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
+ g_autoptr(IdeRunContext) run_context = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
+ g_autoptr(GSettings) settings = NULL;
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autofree char *locality = NULL;
+ IdeContext *context;
+ IdeRuntime *runtime;
- g_return_if_fail (IDE_IS_TEST (self));
+ IDE_ENTRY;
- if (g_strcmp0 (id, priv->id) != 0)
- {
- g_free (priv->id);
- priv->id = g_strdup (id);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]);
- }
-}
+ g_return_if_fail (IDE_IS_TEST (self));
+ g_return_if_fail (IDE_IS_PIPELINE (pipeline));
+ g_return_if_fail (!pty || VTE_IS_PTY (pty));
+ g_return_if_fail (IDE_IS_RUN_COMMAND (self->run_command));
-IdeTestStatus
-ide_test_get_status (IdeTest *self)
-{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_test_run_async);
- g_return_val_if_fail (IDE_IS_TEST (self), 0);
+ if (ide_task_return_error_if_cancelled (task))
+ IDE_EXIT;
- return priv->status;
-}
+ context = ide_object_get_context (IDE_OBJECT (pipeline));
+ runtime = ide_pipeline_get_runtime (pipeline);
+ settings = ide_context_ref_project_settings (context);
+ locality = g_settings_get_string (settings, "unit-test-locality");
-void
-ide_test_set_status (IdeTest *self,
- IdeTestStatus status)
-{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
+ if (ide_str_equal0 (locality, "runtime"))
+ {
+ run_context = ide_run_context_new ();
+ ide_runtime_prepare_to_run (runtime, pipeline, run_context);
+ ide_run_command_prepare_to_run (self->run_command, run_context, context);
+ }
+ else /* "pipeline" */
+ {
+ run_context = ide_pipeline_create_run_context (pipeline, self->run_command);
+ }
- g_return_if_fail (IDE_IS_TEST (self));
+ if (pty != NULL)
+ ide_run_context_set_pty (run_context, pty);
- if (priv->status != status)
+ if (!(subprocess = ide_run_context_spawn (run_context, &error)))
{
- priv->status = status;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_STATUS]);
+ ide_test_set_status (self, IDE_TEST_STATUS_FAILED);
+ ide_task_return_error (task, g_steal_pointer (&error));
+ IDE_EXIT;
+ }
+ else
+ {
+ ide_test_set_status (self, IDE_TEST_STATUS_RUNNING);
+ ide_subprocess_wait_check_async (subprocess,
+ cancellable,
+ ide_test_wait_check_cb,
+ g_steal_pointer (&task));
+ IDE_EXIT;
}
}
-const gchar *
-ide_test_get_icon_name (IdeTest *self)
+gboolean
+ide_test_run_finish (IdeTest *self,
+ GAsyncResult *result,
+ GError **error)
{
- IdeTestPrivate *priv = ide_test_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_TEST (self), NULL);
-
- switch (priv->status)
- {
- case IDE_TEST_STATUS_NONE:
- return "builder-unit-tests-symbolic";
+ gboolean ret;
- case IDE_TEST_STATUS_RUNNING:
- return "builder-unit-tests-running-symbolic";
+ IDE_ENTRY;
- case IDE_TEST_STATUS_FAILED:
- return "builder-unit-tests-fail-symbolic";
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_TEST (self));
+ g_assert (IDE_IS_TASK (result));
- case IDE_TEST_STATUS_SUCCESS:
- return "builder-unit-tests-pass-symbolic";
+ ret = ide_task_propagate_boolean (IDE_TASK (result), error);
- default:
- g_return_val_if_reached (NULL);
- }
+ IDE_RETURN (ret);
}
diff --git a/src/libide/foundry/ide-test.h b/src/libide/foundry/ide-test.h
index f6423ca98..de4883ce1 100644
--- a/src/libide/foundry/ide-test.h
+++ b/src/libide/foundry/ide-test.h
@@ -24,8 +24,12 @@
# error "Only <libide-foundry.h> can be included directly."
#endif
+#include <vte/vte.h>
+
#include <libide-core.h>
+#include "ide-foundry-types.h"
+
G_BEGIN_DECLS
#define IDE_TYPE_TEST (ide_test_get_type())
@@ -38,40 +42,31 @@ typedef enum
IDE_TEST_STATUS_FAILED,
} IdeTestStatus;
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdeTest, ide_test, IDE, TEST, GObject)
-
-struct _IdeTestClass
-{
- GObjectClass parent;
-
- /*< private >*/
- gpointer _reserved[16];
-};
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeTest, ide_test, IDE, TEST, GObject)
-IDE_AVAILABLE_IN_3_32
-IdeTest *ide_test_new (void);
-IDE_AVAILABLE_IN_3_32
-const gchar *ide_test_get_display_name (IdeTest *self);
-IDE_AVAILABLE_IN_3_32
-void ide_test_set_display_name (IdeTest *self,
- const gchar *display_name);
-IDE_AVAILABLE_IN_3_32
-const gchar *ide_test_get_group (IdeTest *self);
-IDE_AVAILABLE_IN_3_32
-void ide_test_set_group (IdeTest *self,
- const gchar *group);
-IDE_AVAILABLE_IN_3_32
-const gchar *ide_test_get_icon_name (IdeTest *self);
-IDE_AVAILABLE_IN_3_32
-const gchar *ide_test_get_id (IdeTest *self);
-IDE_AVAILABLE_IN_3_32
-void ide_test_set_id (IdeTest *self,
- const gchar *id);
-IDE_AVAILABLE_IN_3_32
-IdeTestStatus ide_test_get_status (IdeTest *self);
-IDE_AVAILABLE_IN_3_32
-void ide_test_set_status (IdeTest *self,
- IdeTestStatus status);
+IDE_AVAILABLE_IN_ALL
+IdeTest *ide_test_new (IdeRunCommand *run_command);
+IDE_AVAILABLE_IN_ALL
+const char *ide_test_get_id (IdeTest *self);
+IDE_AVAILABLE_IN_ALL
+IdeTestStatus ide_test_get_status (IdeTest *self);
+IDE_AVAILABLE_IN_ALL
+const char *ide_test_get_title (IdeTest *self);
+IDE_AVAILABLE_IN_ALL
+const char *ide_test_get_icon_name (IdeTest *self);
+IDE_AVAILABLE_IN_ALL
+IdeRunCommand *ide_test_get_run_command (IdeTest *self);
+IDE_AVAILABLE_IN_ALL
+void ide_test_run_async (IdeTest *self,
+ IdePipeline *pipeline,
+ VtePty *pty,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_test_run_finish (IdeTest *self,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
diff --git a/src/libide/foundry/libide-foundry.h b/src/libide/foundry/libide-foundry.h
index 7d73248ef..6b0ecdda0 100644
--- a/src/libide/foundry/libide-foundry.h
+++ b/src/libide/foundry/libide-foundry.h
@@ -55,6 +55,10 @@ G_BEGIN_DECLS
#include "ide-pipeline-stage-transfer.h"
#include "ide-pipeline-stage.h"
#include "ide-pipeline.h"
+#include "ide-run-command.h"
+#include "ide-run-command-provider.h"
+#include "ide-run-commands.h"
+#include "ide-run-context.h"
#include "ide-run-manager.h"
#include "ide-runtime-manager.h"
#include "ide-runtime-provider.h"
@@ -63,7 +67,6 @@ G_BEGIN_DECLS
#include "ide-simple-build-target.h"
#include "ide-simple-toolchain.h"
#include "ide-test-manager.h"
-#include "ide-test-provider.h"
#include "ide-test.h"
#include "ide-toolchain-manager.h"
#include "ide-toolchain-provider.h"
diff --git a/src/libide/foundry/meson.build b/src/libide/foundry/meson.build
index 56fae1a26..7b591bda2 100644
--- a/src/libide/foundry/meson.build
+++ b/src/libide/foundry/meson.build
@@ -50,7 +50,6 @@ libide_foundry_public_headers = [
'ide-simple-toolchain.h',
'ide-test.h',
'ide-test-manager.h',
- 'ide-test-provider.h',
'ide-toolchain-manager.h',
'ide-toolchain-provider.h',
'ide-toolchain.h',
@@ -122,7 +121,6 @@ libide_foundry_public_sources = [
'ide-simple-build-target.c',
'ide-simple-toolchain.c',
'ide-test-manager.c',
- 'ide-test-provider.c',
'ide-test.c',
'ide-toolchain-manager.c',
'ide-toolchain-provider.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]