1 /* ide-run-manager.c
2  *
3  * Copyright 2016-2019 Christian Hergert <chergert@redhat.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * SPDX-License-Identifier: GPL-3.0-or-later
19  */
20 
21 #define G_LOG_DOMAIN "ide-run-manager"
22 
23 #include "config.h"
24 
25 #include <glib/gi18n.h>
26 #include <libide-threading.h>
27 #include <libide-vcs.h>
28 #include <libpeas/peas.h>
29 #include <libpeas/peas-autocleanups.h>
30 
31 #include "ide-private.h"
32 
33 #include "ide-build-manager.h"
34 #include "ide-build-system.h"
35 #include "ide-build-target-provider.h"
36 #include "ide-build-target.h"
37 #include "ide-config-manager.h"
38 #include "ide-config.h"
39 #include "ide-device-manager.h"
40 #include "ide-foundry-compat.h"
41 #include "ide-run-manager-private.h"
42 #include "ide-run-manager.h"
43 #include "ide-runner.h"
44 #include "ide-runtime.h"
45 
46 struct _IdeRunManager
47 {
48   IdeObject                parent_instance;
49 
50   GCancellable            *cancellable;
51   IdeBuildTarget          *build_target;
52   IdeNotification         *notif;
53 
54   const IdeRunHandlerInfo *handler;
55   GList                   *handlers;
56 
57   /* Keep track of last change sequence from the file monitor
58    * so that we can maybe skip past install phase and make
59    * secondary execution time faster.
60    */
61   guint64                  last_change_seq;
62   guint64                  pending_last_change_seq;
63 
64   guint                    busy : 1;
65 };
66 
67 typedef struct
68 {
69   GList     *providers;
70   GPtrArray *results;
71   guint      active;
72 } DiscoverState;
73 
74 static void initable_iface_init                      (GInitableIface *iface);
75 static void ide_run_manager_actions_run              (IdeRunManager  *self,
76                                                       GVariant       *param);
77 static void ide_run_manager_actions_run_with_handler (IdeRunManager  *self,
78                                                       GVariant       *param);
79 static void ide_run_manager_actions_stop             (IdeRunManager  *self,
80                                                       GVariant       *param);
81 
82 DZL_DEFINE_ACTION_GROUP (IdeRunManager, ide_run_manager, {
83   { "run", ide_run_manager_actions_run },
84   { "run-with-handler", ide_run_manager_actions_run_with_handler, "s" },
85   { "stop", ide_run_manager_actions_stop },
86 })
87 
88 G_DEFINE_TYPE_EXTENDED (IdeRunManager, ide_run_manager, IDE_TYPE_OBJECT, G_TYPE_FLAG_FINAL,
89                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
90                         G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
91                                                ide_run_manager_init_action_group))
92 
93 enum {
94   PROP_0,
95   PROP_BUSY,
96   PROP_HANDLER,
97   PROP_BUILD_TARGET,
98   N_PROPS
99 };
100 
101 enum {
102   RUN,
103   STOPPED,
104   N_SIGNALS
105 };
106 
107 static GParamSpec *properties [N_PROPS];
108 static guint signals [N_SIGNALS];
109 
110 static void
discover_state_free(gpointer data)111 discover_state_free (gpointer data)
112 {
113   DiscoverState *state = data;
114 
115   g_assert (state->active == 0);
116 
117   g_list_free_full (state->providers, g_object_unref);
118   g_clear_pointer (&state->results, g_ptr_array_unref);
119   g_slice_free (DiscoverState, state);
120 }
121 
122 static void
ide_run_manager_real_run(IdeRunManager * self,IdeRunner * runner)123 ide_run_manager_real_run (IdeRunManager *self,
124                           IdeRunner     *runner)
125 {
126   g_assert (IDE_IS_RUN_MANAGER (self));
127   g_assert (IDE_IS_RUNNER (runner));
128 
129   /*
130    * If the current handler has a callback specified (our default "run" handler
131    * does not), then we need to allow that handler to prepare the runner.
132    */
133   if (self->handler != NULL && self->handler->handler != NULL)
134     self->handler->handler (self, runner, self->handler->handler_data);
135 }
136 
137 static void
ide_run_handler_info_free(gpointer data)138 ide_run_handler_info_free (gpointer data)
139 {
140   IdeRunHandlerInfo *info = data;
141 
142   g_free (info->id);
143   g_free (info->title);
144   g_free (info->icon_name);
145   g_free (info->accel);
146 
147   if (info->handler_data_destroy)
148     info->handler_data_destroy (info->handler_data);
149 
150   g_slice_free (IdeRunHandlerInfo, info);
151 }
152 
153 static void
ide_run_manager_dispose(GObject * object)154 ide_run_manager_dispose (GObject *object)
155 {
156   IdeRunManager *self = (IdeRunManager *)object;
157 
158   self->handler = NULL;
159 
160   g_clear_object (&self->cancellable);
161   ide_clear_and_destroy_object (&self->build_target);
162 
163   g_list_free_full (self->handlers, ide_run_handler_info_free);
164   self->handlers = NULL;
165 
166   G_OBJECT_CLASS (ide_run_manager_parent_class)->dispose (object);
167 }
168 
169 static void
ide_run_manager_update_action_enabled(IdeRunManager * self)170 ide_run_manager_update_action_enabled (IdeRunManager *self)
171 {
172   IdeBuildManager *build_manager;
173   IdeContext *context;
174   gboolean can_build;
175 
176   g_assert (IDE_IS_RUN_MANAGER (self));
177 
178   context = ide_object_get_context (IDE_OBJECT (self));
179   build_manager = ide_build_manager_from_context (context);
180   can_build = ide_build_manager_get_can_build (build_manager);
181 
182   ide_run_manager_set_action_enabled (self, "run",
183                                       self->busy == FALSE && can_build == TRUE);
184   ide_run_manager_set_action_enabled (self, "run-with-handler",
185                                       self->busy == FALSE && can_build == TRUE);
186   ide_run_manager_set_action_enabled (self, "stop", self->busy == TRUE);
187 }
188 
189 static void
ide_run_manager_notify_can_build(IdeRunManager * self,GParamSpec * pspec,IdeBuildManager * build_manager)190 ide_run_manager_notify_can_build (IdeRunManager   *self,
191                                   GParamSpec      *pspec,
192                                   IdeBuildManager *build_manager)
193 {
194   IDE_ENTRY;
195 
196   g_assert (IDE_IS_RUN_MANAGER (self));
197   g_assert (G_IS_PARAM_SPEC (pspec));
198   g_assert (IDE_IS_BUILD_MANAGER (build_manager));
199 
200   ide_run_manager_update_action_enabled (self);
201 
202   IDE_EXIT;
203 }
204 
205 static gboolean
initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)206 initable_init (GInitable     *initable,
207                GCancellable  *cancellable,
208                GError       **error)
209 {
210   IdeRunManager *self = (IdeRunManager *)initable;
211   IdeBuildManager *build_manager;
212   IdeContext *context;
213 
214   IDE_ENTRY;
215 
216   g_assert (IDE_IS_RUN_MANAGER (self));
217   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
218 
219   context = ide_object_get_context (IDE_OBJECT (self));
220   build_manager = ide_build_manager_from_context (context);
221 
222   g_signal_connect_object (build_manager,
223                            "notify::can-build",
224                            G_CALLBACK (ide_run_manager_notify_can_build),
225                            self,
226                            G_CONNECT_SWAPPED);
227 
228   ide_run_manager_update_action_enabled (self);
229 
230   IDE_RETURN (TRUE);
231 }
232 
233 static void
initable_iface_init(GInitableIface * iface)234 initable_iface_init (GInitableIface *iface)
235 {
236   iface->init = initable_init;
237 }
238 
239 static void
ide_run_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)240 ide_run_manager_get_property (GObject    *object,
241                               guint       prop_id,
242                               GValue     *value,
243                               GParamSpec *pspec)
244 {
245   IdeRunManager *self = IDE_RUN_MANAGER (object);
246 
247   switch (prop_id)
248     {
249     case PROP_BUSY:
250       g_value_set_boolean (value, ide_run_manager_get_busy (self));
251       break;
252 
253     case PROP_HANDLER:
254       g_value_set_string (value, ide_run_manager_get_handler (self));
255       break;
256 
257     case PROP_BUILD_TARGET:
258       g_value_set_object (value, ide_run_manager_get_build_target (self));
259       break;
260 
261     default:
262       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
263     }
264 }
265 
266 static void
ide_run_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)267 ide_run_manager_set_property (GObject      *object,
268                               guint         prop_id,
269                               const GValue *value,
270                               GParamSpec   *pspec)
271 {
272   IdeRunManager *self = IDE_RUN_MANAGER (object);
273 
274   switch (prop_id)
275     {
276     case PROP_BUILD_TARGET:
277       ide_run_manager_set_build_target (self, g_value_get_object (value));
278       break;
279 
280     default:
281       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
282     }
283 }
284 
285 static void
ide_run_manager_class_init(IdeRunManagerClass * klass)286 ide_run_manager_class_init (IdeRunManagerClass *klass)
287 {
288   GObjectClass *object_class = G_OBJECT_CLASS (klass);
289 
290   object_class->dispose = ide_run_manager_dispose;
291   object_class->get_property = ide_run_manager_get_property;
292   object_class->set_property = ide_run_manager_set_property;
293 
294   properties [PROP_BUSY] =
295     g_param_spec_boolean ("busy",
296                           "Busy",
297                           "Busy",
298                           FALSE,
299                           (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
300 
301   properties [PROP_HANDLER] =
302     g_param_spec_string ("handler",
303                          "Handler",
304                          "Handler",
305                          "run",
306                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
307 
308   properties [PROP_BUILD_TARGET] =
309     g_param_spec_object ("build-target",
310                          "Build Target",
311                          "The IdeBuildTarget that will be run",
312                          IDE_TYPE_BUILD_TARGET,
313                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
314 
315   g_object_class_install_properties (object_class, N_PROPS, properties);
316 
317   /**
318    * IdeRunManager::run:
319    * @self: An #IdeRunManager
320    * @runner: An #IdeRunner
321    *
322    * This signal is emitted right before ide_runner_run_async() is called
323    * on an #IdeRunner. It can be used by plugins to tweak things right
324    * before the runner is executed.
325    *
326    * The current run handler (debugger, profiler, etc) is run as the default
327    * handler for this function. So connect with %G_SIGNAL_AFTER if you want
328    * to be nofied after the run handler has executed. It's unwise to change
329    * things that the run handler might expect. Generally if you want to
330    * change settings, do that before the run handler has exected.
331    *
332    * Since: 3.32
333    */
334   signals [RUN] =
335     g_signal_new_class_handler ("run",
336                                 G_TYPE_FROM_CLASS (klass),
337                                 G_SIGNAL_RUN_LAST,
338                                 G_CALLBACK (ide_run_manager_real_run),
339                                 NULL,
340                                 NULL,
341                                 NULL,
342                                 G_TYPE_NONE,
343                                 1,
344                                 IDE_TYPE_RUNNER);
345 
346   /**
347    * IdeRunManager::stopped:
348    *
349    * This signal is emitted when the run manager has stopped the currently
350    * executing inferior.
351    *
352    * Since: 3.32
353    */
354   signals [STOPPED] =
355     g_signal_new ("stopped",
356                   G_TYPE_FROM_CLASS (klass),
357                   G_SIGNAL_RUN_LAST,
358                   0,
359                   NULL,
360                   NULL,
361                   NULL,
362                   G_TYPE_NONE,
363                   0);
364 }
365 
366 gboolean
ide_run_manager_get_busy(IdeRunManager * self)367 ide_run_manager_get_busy (IdeRunManager *self)
368 {
369   g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), FALSE);
370 
371   return self->busy;
372 }
373 
374 static gboolean
ide_run_manager_check_busy(IdeRunManager * self,GError ** error)375 ide_run_manager_check_busy (IdeRunManager  *self,
376                             GError        **error)
377 {
378   g_assert (IDE_IS_RUN_MANAGER (self));
379   g_assert (error != NULL);
380 
381   if (ide_run_manager_get_busy (self))
382     {
383       g_set_error (error,
384                    G_IO_ERROR,
385                    G_IO_ERROR_BUSY,
386                    "%s",
387                    _("Cannot run target, another target is running"));
388       return TRUE;
389     }
390 
391   return FALSE;
392 }
393 
394 static void
ide_run_manager_run_cb(GObject * object,GAsyncResult * result,gpointer user_data)395 ide_run_manager_run_cb (GObject      *object,
396                         GAsyncResult *result,
397                         gpointer      user_data)
398 {
399   IdeRunner *runner = (IdeRunner *)object;
400   g_autoptr(IdeTask) task = user_data;
401   g_autoptr(GError) error = NULL;
402   IdeRunManager *self;
403 
404   IDE_ENTRY;
405 
406   g_assert (IDE_IS_RUNNER (runner));
407   g_assert (IDE_IS_TASK (task));
408 
409   self = ide_task_get_source_object (task);
410 
411   if (self->notif != NULL)
412     {
413       ide_notification_withdraw (self->notif);
414       g_clear_object (&self->notif);
415     }
416 
417   if (!ide_runner_run_finish (runner, result, &error))
418     ide_task_return_error (task, g_steal_pointer (&error));
419   else
420     ide_task_return_boolean (task, TRUE);
421 
422   g_signal_emit (self, signals [STOPPED], 0);
423 
424   ide_object_destroy (IDE_OBJECT (runner));
425 
426   IDE_EXIT;
427 }
428 
429 static void
copy_builtin_envvars(IdeEnvironment * environment)430 copy_builtin_envvars (IdeEnvironment *environment)
431 {
432   static const gchar *copy_env[] = {
433     "AT_SPI_BUS_ADDRESS",
434     "COLORTERM",
435     "DBUS_SESSION_BUS_ADDRESS",
436     "DBUS_SYSTEM_BUS_ADDRESS",
437     "DESKTOP_SESSION",
438     "DISPLAY",
439     "LANG",
440     "SHELL",
441     "SSH_AUTH_SOCK",
442     "USER",
443     "WAYLAND_DISPLAY",
444     "XAUTHORITY",
445     "XDG_CURRENT_DESKTOP",
446     "XDG_MENU_PREFIX",
447 #if 0
448     /* Can't copy these as they could mess up Flatpak */
449     "XDG_DATA_DIRS",
450     "XDG_RUNTIME_DIR",
451 #endif
452     "XDG_SEAT",
453     "XDG_SESSION_DESKTOP",
454     "XDG_SESSION_ID",
455     "XDG_SESSION_TYPE",
456     "XDG_VTNR",
457   };
458   const gchar * const *host_environ = _ide_host_environ ();
459 
460   for (guint i = 0; i < G_N_ELEMENTS (copy_env); i++)
461     {
462       const gchar *key = copy_env[i];
463       const gchar *val = g_environ_getenv ((gchar **)host_environ, key);
464 
465       if (val != NULL && ide_environment_getenv (environment, key) == NULL)
466         ide_environment_setenv (environment, key, val);
467     }
468 }
469 
470 static void
create_runner_cb(GObject * object,GAsyncResult * result,gpointer user_data)471 create_runner_cb (GObject      *object,
472                   GAsyncResult *result,
473                   gpointer      user_data)
474 {
475   IdeRunManager *self;
476   IdeDeviceManager *device_manager = (IDE_DEVICE_MANAGER (object));
477   g_autoptr(IdeTask) task = user_data;
478   g_autoptr(GError) error = NULL;
479   g_autofree gchar *name = NULL;
480   g_autofree gchar *title = NULL;
481   IdeBuildTarget *build_target;
482   IdeContext *context;
483   IdeConfigManager *config_manager;
484   IdeConfig *config;
485   IdeEnvironment *environment;
486   IdeRuntime *runtime;
487   g_autoptr(IdeRunner) runner = NULL;
488   GCancellable *cancellable;
489   const gchar *run_opts;
490 
491   IDE_ENTRY;
492 
493   g_assert (IDE_IS_DEVICE_MANAGER (device_manager));
494   g_assert (G_IS_ASYNC_RESULT (result));
495   g_assert (IDE_IS_TASK (task));
496 
497   self = ide_task_get_source_object (task);
498   g_assert (IDE_IS_RUN_MANAGER (self));
499 
500   runner = ide_device_manager_create_runner_finish (device_manager, result, &error);
501 
502   if (error != NULL)
503     {
504       ide_task_return_error (task, g_steal_pointer (&error));
505       IDE_EXIT;
506     }
507 
508   build_target = ide_task_get_task_data (task);
509   context = ide_object_get_context (IDE_OBJECT (self));
510 
511   g_assert (IDE_IS_BUILD_TARGET (build_target));
512   g_assert (IDE_IS_CONTEXT (context));
513 
514   config_manager = ide_config_manager_from_context (context);
515   config = ide_config_manager_get_current (config_manager);
516   runtime = ide_config_get_runtime (config);
517 
518   if (runner == NULL)
519     {
520       if (runtime == NULL)
521         {
522           ide_task_return_new_error (task,
523                                      IDE_RUNTIME_ERROR,
524                                      IDE_RUNTIME_ERROR_NO_SUCH_RUNTIME,
525                                      "%s “%s”",
526                                      _("Failed to locate runtime"),
527                                      ide_config_get_runtime_id (config));
528           IDE_EXIT;
529         }
530 
531       runner = ide_runtime_create_runner (runtime, build_target);
532     }
533 
534   cancellable = ide_task_get_cancellable (task);
535 
536   g_assert (IDE_IS_RUNNER (runner));
537   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
538 
539   /* Add our run arguments if specified in the config. */
540   if (NULL != (run_opts = ide_config_get_run_opts (config)))
541     {
542       g_auto(GStrv) argv = NULL;
543       gint argc;
544 
545       if (g_shell_parse_argv (run_opts, &argc, &argv, NULL))
546         {
547           for (gint i = 0; i < argc; i++)
548             ide_runner_append_argv (runner, argv[i]);
549         }
550     }
551 
552   /* Add our runtime environment variables. */
553   environment = ide_runner_get_environment (runner);
554   copy_builtin_envvars (environment);
555   ide_environment_copy_into (ide_config_get_runtime_environment (config), environment, TRUE);
556 
557   g_signal_emit (self, signals [RUN], 0, runner);
558 
559   if (ide_runner_get_failed (runner))
560     {
561       ide_task_return_new_error (task,
562                                  IDE_RUNTIME_ERROR,
563                                  IDE_RUNTIME_ERROR_SPAWN_FAILED,
564                                  "Failed to execute the application");
565       IDE_EXIT;
566     }
567 
568   if (self->notif != NULL)
569     {
570       ide_notification_withdraw (self->notif);
571       g_clear_object (&self->notif);
572     }
573 
574   self->notif = ide_notification_new ();
575   name = ide_build_target_get_name (build_target);
576   /* translators: %s is replaced with the name of the users executable */
577   title = g_strdup_printf (_("Running %s…"), name);
578   ide_notification_set_title (self->notif, title);
579   ide_notification_attach (self->notif, IDE_OBJECT (self));
580 
581   ide_runner_run_async (runner,
582                         cancellable,
583                         ide_run_manager_run_cb,
584                         g_object_ref (task));
585 
586   IDE_EXIT;
587 }
588 
589 static void
deploy_cb(GObject * object,GAsyncResult * result,gpointer user_data)590 deploy_cb (GObject      *object,
591            GAsyncResult *result,
592            gpointer      user_data)
593 {
594   IdeDeviceManager *device_manager = (IdeDeviceManager *)object;
595   IdeBuildManager *build_manager;
596   IdeContext *context;
597   IdePipeline *pipeline;
598   IdeRunManager *self;
599   g_autoptr(IdeTask) task = user_data;
600   g_autoptr(GError) error = NULL;
601 
602   IDE_ENTRY;
603 
604   g_assert (IDE_IS_DEVICE_MANAGER (device_manager));
605   g_assert (IDE_IS_TASK (task));
606 
607   if (!ide_device_manager_deploy_finish (device_manager, result, &error))
608     {
609       ide_task_return_error (task, g_steal_pointer (&error));
610       IDE_EXIT;
611     }
612 
613   self = ide_task_get_source_object (task);
614   g_assert (IDE_IS_RUN_MANAGER (self));
615 
616   context = ide_object_get_context (IDE_OBJECT (self));
617   g_assert (IDE_IS_CONTEXT (context));
618 
619   build_manager = ide_build_manager_from_context (context);
620   pipeline = ide_build_manager_get_pipeline (build_manager);
621 
622   ide_device_manager_create_runner_async (device_manager,
623                                           pipeline,
624                                           ide_task_get_cancellable (task),
625                                           create_runner_cb,
626                                           g_object_ref (task));
627 
628   IDE_EXIT;
629 }
630 
631 static void
do_run_async(IdeRunManager * self,IdeTask * task)632 do_run_async (IdeRunManager *self,
633               IdeTask       *task)
634 {
635   IdeBuildManager *build_manager;
636   IdeContext *context;
637   IdeDeviceManager *device_manager;
638   IdePipeline *pipeline;
639   GCancellable *cancellable;
640 
641   IDE_ENTRY;
642 
643   g_assert (IDE_IS_RUN_MANAGER (self));
644   g_assert (IDE_IS_TASK (task));
645 
646   context = ide_object_get_context (IDE_OBJECT (self));
647   g_assert (IDE_IS_CONTEXT (context));
648 
649   build_manager = ide_build_manager_from_context (context);
650   pipeline = ide_build_manager_get_pipeline (build_manager);
651   device_manager = ide_device_manager_from_context (context);
652 
653   cancellable = ide_task_get_cancellable (task);
654   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
655 
656   ide_device_manager_deploy_async (device_manager,
657                                    pipeline,
658                                    cancellable,
659                                    deploy_cb,
660                                    g_object_ref (task));
661 
662   IDE_EXIT;
663 }
664 
665 static void
ide_run_manager_run_discover_cb(GObject * object,GAsyncResult * result,gpointer user_data)666 ide_run_manager_run_discover_cb (GObject      *object,
667                                  GAsyncResult *result,
668                                  gpointer      user_data)
669 {
670   IdeRunManager *self = (IdeRunManager *)object;
671   g_autoptr(IdeBuildTarget) build_target = NULL;
672   g_autoptr(IdeTask) task = user_data;
673   g_autoptr(GError) error = NULL;
674 
675   IDE_ENTRY;
676 
677   g_assert (IDE_IS_RUN_MANAGER (self));
678   g_assert (G_IS_ASYNC_RESULT (result));
679 
680   build_target = ide_run_manager_discover_default_target_finish (self, result, &error);
681 
682   if (build_target == NULL)
683     {
684       ide_task_return_error (task, g_steal_pointer (&error));
685       IDE_EXIT;
686     }
687 
688   ide_run_manager_set_build_target (self, build_target);
689 
690   ide_task_set_task_data (task, g_steal_pointer (&build_target), g_object_unref);
691 
692   do_run_async (self, task);
693 
694   IDE_EXIT;
695 }
696 
697 static void
ide_run_manager_install_cb(GObject * object,GAsyncResult * result,gpointer user_data)698 ide_run_manager_install_cb (GObject      *object,
699                             GAsyncResult *result,
700                             gpointer      user_data)
701 {
702   IdeBuildManager *build_manager = (IdeBuildManager *)object;
703   g_autoptr(IdeTask) task = user_data;
704   g_autoptr(GError) error = NULL;
705   IdeRunManager *self;
706   IdeBuildTarget *build_target;
707   GCancellable *cancellable;
708 
709   IDE_ENTRY;
710 
711   g_assert (IDE_IS_BUILD_MANAGER (build_manager));
712   g_assert (IDE_IS_TASK (task));
713 
714   self = ide_task_get_source_object (task);
715   g_assert (IDE_IS_RUN_MANAGER (self));
716 
717   if (!ide_build_manager_build_finish (build_manager, result, &error))
718     {
719       /* We want to let the consumer know there was a build error
720        * (but don't need to pass the specific error code) so that
721        * they have an error code to check against.
722        */
723       ide_task_return_new_error (task,
724                                  IDE_RUNTIME_ERROR,
725                                  IDE_RUNTIME_ERROR_BUILD_FAILED,
726                                  /* translators: %s is replaced with the specific error reason */
727                                  _("The build target failed to build: %s"),
728                                  error->message);
729       IDE_EXIT;
730     }
731 
732   self->last_change_seq = self->pending_last_change_seq;
733 
734   build_target = ide_run_manager_get_build_target (self);
735 
736   if (build_target == NULL)
737     {
738       cancellable = ide_task_get_cancellable (task);
739       g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
740 
741       ide_run_manager_discover_default_target_async (self,
742                                                      cancellable,
743                                                      ide_run_manager_run_discover_cb,
744                                                      g_steal_pointer (&task));
745       IDE_EXIT;
746     }
747 
748   ide_task_set_task_data (task, g_object_ref (build_target), g_object_unref);
749 
750   do_run_async (self, task);
751 
752   IDE_EXIT;
753 }
754 
755 static void
ide_run_manager_task_completed(IdeRunManager * self,GParamSpec * pspec,IdeTask * task)756 ide_run_manager_task_completed (IdeRunManager *self,
757                                 GParamSpec    *pspec,
758                                 IdeTask       *task)
759 {
760   IDE_ENTRY;
761 
762   g_assert (IDE_IS_RUN_MANAGER (self));
763   g_assert (pspec != NULL);
764   g_assert (IDE_IS_TASK (task));
765 
766   self->busy = FALSE;
767   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
768 
769   ide_run_manager_update_action_enabled (self);
770 
771   IDE_EXIT;
772 }
773 
774 static void
ide_run_manager_do_install_before_run(IdeRunManager * self,IdeTask * task)775 ide_run_manager_do_install_before_run (IdeRunManager *self,
776                                        IdeTask       *task)
777 {
778   g_autoptr(IdeContext) context = NULL;
779   IdeBuildManager *build_manager;
780   IdeVcsMonitor *monitor;
781   guint64 sequence = 0;
782 
783   IDE_ENTRY;
784 
785   g_assert (IDE_IS_MAIN_THREAD ());
786   g_assert (IDE_IS_RUN_MANAGER (self));
787   g_assert (IDE_IS_TASK (task));
788 
789   context = ide_object_ref_context (IDE_OBJECT (self));
790   build_manager = ide_build_manager_from_context (context);
791   monitor = ide_vcs_monitor_from_context (context);
792 
793   /*
794    * First we need to make sure the target is up to date and installed
795    * so that all the dependent resources are available.
796    */
797 
798   self->busy = TRUE;
799   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
800 
801   g_signal_connect_object (task,
802                            "notify::completed",
803                            G_CALLBACK (ide_run_manager_task_completed),
804                            self,
805                            G_CONNECT_SWAPPED);
806 
807   if (monitor != NULL)
808     sequence = ide_vcs_monitor_get_sequence (monitor);
809 
810   /*
811    * If we detect that nothing has changed in the project directory since the
812    * last time we ran, we can avoid installing the project. This will not help
813    * in situations where external resources have changed outside of builders
814    * control, but users can simply force a Build in that case.
815    */
816   if (self->build_target != NULL && sequence == self->last_change_seq)
817     {
818       g_debug ("Skipping install phase as no files appear to have changed "
819                "(sequence %"G_GUINT64_FORMAT")", sequence);
820       ide_run_manager_update_action_enabled (self);
821       ide_task_set_task_data (task, g_object_ref (self->build_target), g_object_unref);
822       do_run_async (self, task);
823       IDE_EXIT;
824     }
825 
826   self->pending_last_change_seq = sequence;
827 
828   ide_build_manager_build_async (build_manager,
829                                  IDE_PIPELINE_PHASE_INSTALL,
830                                  NULL,
831                                  ide_task_get_cancellable (task),
832                                  ide_run_manager_install_cb,
833                                  g_object_ref (task));
834 
835   ide_run_manager_update_action_enabled (self);
836 
837   IDE_EXIT;
838 }
839 
840 void
ide_run_manager_run_async(IdeRunManager * self,IdeBuildTarget * build_target,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)841 ide_run_manager_run_async (IdeRunManager       *self,
842                            IdeBuildTarget      *build_target,
843                            GCancellable        *cancellable,
844                            GAsyncReadyCallback  callback,
845                            gpointer             user_data)
846 {
847   g_autoptr(IdeTask) task = NULL;
848   g_autoptr(GCancellable) local_cancellable = NULL;
849   g_autoptr(GError) error = NULL;
850 
851   IDE_ENTRY;
852 
853   g_return_if_fail (IDE_IS_MAIN_THREAD ());
854   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
855   g_return_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target));
856   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
857   g_return_if_fail (!g_cancellable_is_cancelled (self->cancellable));
858 
859   if (cancellable == NULL)
860     cancellable = local_cancellable = g_cancellable_new ();
861 
862   dzl_cancellable_chain (cancellable, self->cancellable);
863 
864   task = ide_task_new (self, cancellable, callback, user_data);
865   ide_task_set_source_tag (task, ide_run_manager_run_async);
866   ide_task_set_priority (task, G_PRIORITY_LOW);
867 
868   if (ide_task_return_error_if_cancelled (task))
869     IDE_EXIT;
870 
871   if (ide_run_manager_check_busy (self, &error))
872     {
873       ide_task_return_error (task, g_steal_pointer (&error));
874       IDE_EXIT;
875     }
876 
877   if (build_target != NULL)
878     ide_run_manager_set_build_target (self, build_target);
879 
880   ide_run_manager_do_install_before_run (self, task);
881 
882   IDE_EXIT;
883 }
884 
885 gboolean
ide_run_manager_run_finish(IdeRunManager * self,GAsyncResult * result,GError ** error)886 ide_run_manager_run_finish (IdeRunManager  *self,
887                             GAsyncResult   *result,
888                             GError        **error)
889 {
890   gboolean ret;
891 
892   IDE_ENTRY;
893 
894   g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), FALSE);
895   g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
896 
897   ret = ide_task_propagate_boolean (IDE_TASK (result), error);
898 
899   IDE_RETURN (ret);
900 }
901 
902 static gboolean
do_cancel_in_timeout(gpointer user_data)903 do_cancel_in_timeout (gpointer user_data)
904 {
905   g_autoptr(GCancellable) cancellable = user_data;
906 
907   IDE_ENTRY;
908 
909   g_assert (G_IS_CANCELLABLE (cancellable));
910 
911   if (!g_cancellable_is_cancelled (cancellable))
912     g_cancellable_cancel (cancellable);
913 
914   IDE_RETURN (G_SOURCE_REMOVE);
915 }
916 
917 void
ide_run_manager_cancel(IdeRunManager * self)918 ide_run_manager_cancel (IdeRunManager *self)
919 {
920   IDE_ENTRY;
921 
922   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
923 
924   if (self->cancellable != NULL)
925     g_timeout_add (0, do_cancel_in_timeout, g_steal_pointer (&self->cancellable));
926   self->cancellable = g_cancellable_new ();
927 
928   IDE_EXIT;
929 }
930 
931 void
ide_run_manager_set_handler(IdeRunManager * self,const gchar * id)932 ide_run_manager_set_handler (IdeRunManager *self,
933                              const gchar   *id)
934 {
935   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
936 
937   self->handler = NULL;
938 
939   for (GList *iter = self->handlers; iter; iter = iter->next)
940     {
941       const IdeRunHandlerInfo *info = iter->data;
942 
943       if (g_strcmp0 (info->id, id) == 0)
944         {
945           self->handler = info;
946           IDE_TRACE_MSG ("run handler set to %s", info->title);
947           g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HANDLER]);
948           break;
949         }
950     }
951 }
952 
953 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)954 ide_run_manager_add_handler (IdeRunManager  *self,
955                              const gchar    *id,
956                              const gchar    *title,
957                              const gchar    *icon_name,
958                              const gchar    *accel,
959                              IdeRunHandler   run_handler,
960                              gpointer        user_data,
961                              GDestroyNotify  user_data_destroy)
962 {
963   IdeRunHandlerInfo *info;
964   DzlShortcutManager *manager;
965   DzlShortcutTheme *theme;
966   g_autofree gchar *action_name = NULL;
967   GApplication *app;
968 
969   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
970   g_return_if_fail (id != NULL);
971   g_return_if_fail (title != NULL);
972 
973   info = g_slice_new0 (IdeRunHandlerInfo);
974   info->id = g_strdup (id);
975   info->title = g_strdup (title);
976   info->icon_name = g_strdup (icon_name);
977   info->accel = g_strdup (accel);
978   info->handler = run_handler;
979   info->handler_data = user_data;
980   info->handler_data_destroy = user_data_destroy;
981 
982   self->handlers = g_list_append (self->handlers, info);
983 
984   app = g_application_get_default ();
985   manager = dzl_application_get_shortcut_manager (DZL_APPLICATION (app));
986   theme = g_object_ref (dzl_shortcut_manager_get_theme (manager));
987 
988   action_name = g_strdup_printf ("run-manager.run-with-handler('%s')", id);
989 
990   dzl_shortcut_manager_add_action (manager,
991                                    action_name,
992                                    N_("Workbench shortcuts"),
993                                    N_("Build and Run"),
994                                    g_dgettext (GETTEXT_PACKAGE, title),
995                                    NULL);
996 
997   dzl_shortcut_theme_set_accel_for_action (theme,
998                                            action_name,
999                                            accel,
1000                                            DZL_SHORTCUT_PHASE_GLOBAL | DZL_SHORTCUT_PHASE_CAPTURE);
1001 
1002   if (self->handler == NULL)
1003     self->handler = info;
1004 }
1005 
1006 void
ide_run_manager_remove_handler(IdeRunManager * self,const gchar * id)1007 ide_run_manager_remove_handler (IdeRunManager *self,
1008                                 const gchar   *id)
1009 {
1010   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
1011   g_return_if_fail (id != NULL);
1012 
1013   for (GList *iter = self->handlers; iter; iter = iter->next)
1014     {
1015       IdeRunHandlerInfo *info = iter->data;
1016 
1017       if (g_strcmp0 (info->id, id) == 0)
1018         {
1019           self->handlers = g_list_delete_link (self->handlers, iter);
1020 
1021           if (self->handler == info && self->handlers != NULL)
1022             self->handler = self->handlers->data;
1023           else
1024             self->handler = NULL;
1025 
1026           ide_run_handler_info_free (info);
1027 
1028           break;
1029         }
1030     }
1031 }
1032 
1033 /**
1034  * ide_run_manager_get_build_target:
1035  *
1036  * Gets the build target that will be executed by the run manager which
1037  * was either specified to ide_run_manager_run_async() or determined by
1038  * the build system.
1039  *
1040  * Returns: (transfer none): An #IdeBuildTarget or %NULL if no build target
1041  *   has been set.
1042  *
1043  * Since: 3.32
1044  */
1045 IdeBuildTarget *
ide_run_manager_get_build_target(IdeRunManager * self)1046 ide_run_manager_get_build_target (IdeRunManager *self)
1047 {
1048   g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
1049 
1050   return self->build_target;
1051 }
1052 
1053 void
ide_run_manager_set_build_target(IdeRunManager * self,IdeBuildTarget * build_target)1054 ide_run_manager_set_build_target (IdeRunManager  *self,
1055                                   IdeBuildTarget *build_target)
1056 {
1057   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
1058   g_return_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target));
1059 
1060   if (build_target == self->build_target)
1061     return;
1062 
1063   if (self->build_target)
1064     ide_clear_and_destroy_object (&self->build_target);
1065 
1066   if (build_target)
1067     self->build_target = g_object_ref (build_target);
1068 
1069   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUILD_TARGET]);
1070 }
1071 
1072 static gint
compare_targets(gconstpointer a,gconstpointer b)1073 compare_targets (gconstpointer a,
1074                  gconstpointer b)
1075 {
1076   const IdeBuildTarget * const *a_target = a;
1077   const IdeBuildTarget * const *b_target = b;
1078 
1079   return ide_build_target_compare (*a_target, *b_target);
1080 }
1081 
1082 static void
collect_extensions(PeasExtensionSet * set,PeasPluginInfo * plugin_info,PeasExtension * exten,gpointer user_data)1083 collect_extensions (PeasExtensionSet *set,
1084                     PeasPluginInfo   *plugin_info,
1085                     PeasExtension    *exten,
1086                     gpointer          user_data)
1087 {
1088   DiscoverState *state = user_data;
1089 
1090   g_assert (state != NULL);
1091   g_assert (IDE_IS_BUILD_TARGET_PROVIDER (exten));
1092 
1093   state->providers = g_list_append (state->providers, g_object_ref (exten));
1094   state->active++;
1095 }
1096 
1097 static void
ide_run_manager_provider_get_targets_cb(GObject * object,GAsyncResult * result,gpointer user_data)1098 ide_run_manager_provider_get_targets_cb (GObject      *object,
1099                                          GAsyncResult *result,
1100                                          gpointer      user_data)
1101 {
1102   IdeBuildTargetProvider *provider = (IdeBuildTargetProvider *)object;
1103   g_autoptr(IdeBuildTarget) first = NULL;
1104   g_autoptr(IdeTask) task = user_data;
1105   g_autoptr(GPtrArray) ret = NULL;
1106   g_autoptr(GError) error = NULL;
1107   IdeRunManager *self;
1108   DiscoverState *state;
1109 
1110   IDE_ENTRY;
1111 
1112   g_assert (IDE_IS_BUILD_TARGET_PROVIDER (provider));
1113   g_assert (G_IS_ASYNC_RESULT (result));
1114   g_assert (IDE_IS_TASK (task));
1115 
1116   self = ide_task_get_source_object (task);
1117   state = ide_task_get_task_data (task);
1118 
1119   g_assert (IDE_IS_RUN_MANAGER (self));
1120   g_assert (state != NULL);
1121   g_assert (state->active > 0);
1122   g_assert (g_list_find (state->providers, provider) != NULL);
1123 
1124   ret = ide_build_target_provider_get_targets_finish (provider, result, &error);
1125   IDE_PTR_ARRAY_SET_FREE_FUNC (ret, g_object_unref);
1126 
1127   if (ret != NULL)
1128     {
1129       for (guint i = 0; i < ret->len; i++)
1130         {
1131           IdeBuildTarget *target = g_ptr_array_index (ret, i);
1132 
1133           if (ide_object_is_root (IDE_OBJECT (target)))
1134             ide_object_append (IDE_OBJECT (self), IDE_OBJECT (target));
1135 
1136           g_ptr_array_add (state->results, g_object_ref (target));
1137         }
1138     }
1139 
1140   ide_object_destroy (IDE_OBJECT (provider));
1141 
1142   state->active--;
1143 
1144   if (state->active > 0)
1145     return;
1146 
1147   if (state->results->len == 0)
1148     {
1149       if (error != NULL)
1150         ide_task_return_error (task, g_steal_pointer (&error));
1151       else
1152         ide_task_return_new_error (task,
1153                                    IDE_RUNTIME_ERROR,
1154                                    IDE_RUNTIME_ERROR_TARGET_NOT_FOUND,
1155                                    _("Failed to locate a build target"));
1156       IDE_EXIT;
1157     }
1158 
1159   g_ptr_array_sort (state->results, compare_targets);
1160 
1161   /* Steal the first item so that it is not destroyed */
1162   first = ide_ptr_array_steal_index (state->results,
1163                                      0,
1164                                      (GDestroyNotify)ide_object_unref_and_destroy);
1165   ide_task_return_pointer (task,
1166                            IDE_OBJECT (g_steal_pointer (&first)),
1167                            ide_object_unref_and_destroy);
1168 
1169   IDE_EXIT;
1170 }
1171 
1172 void
ide_run_manager_discover_default_target_async(IdeRunManager * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1173 ide_run_manager_discover_default_target_async (IdeRunManager       *self,
1174                                                GCancellable        *cancellable,
1175                                                GAsyncReadyCallback  callback,
1176                                                gpointer             user_data)
1177 {
1178   g_autoptr(PeasExtensionSet) set = NULL;
1179   g_autoptr(IdeTask) task = NULL;
1180   DiscoverState *state;
1181 
1182   IDE_ENTRY;
1183 
1184   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
1185   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
1186 
1187   task = ide_task_new (self, cancellable, callback, user_data);
1188   ide_task_set_source_tag (task, ide_run_manager_discover_default_target_async);
1189   ide_task_set_priority (task, G_PRIORITY_LOW);
1190 
1191   set = peas_extension_set_new (peas_engine_get_default (),
1192                                 IDE_TYPE_BUILD_TARGET_PROVIDER,
1193                                 NULL);
1194 
1195   state = g_slice_new0 (DiscoverState);
1196   state->results = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_object_unref_and_destroy);
1197   state->providers = NULL;
1198   state->active = 0;
1199 
1200   peas_extension_set_foreach (set, collect_extensions, state);
1201 
1202   for (const GList *iter = state->providers; iter; iter = iter->next)
1203     ide_object_append (IDE_OBJECT (self), IDE_OBJECT (iter->data));
1204 
1205   ide_task_set_task_data (task, state, discover_state_free);
1206 
1207   for (const GList *iter = state->providers; iter != NULL; iter = iter->next)
1208     {
1209       IdeBuildTargetProvider *provider = iter->data;
1210 
1211       ide_build_target_provider_get_targets_async (provider,
1212                                                    cancellable,
1213                                                    ide_run_manager_provider_get_targets_cb,
1214                                                    g_object_ref (task));
1215     }
1216 
1217   if (state->active == 0)
1218     ide_task_return_new_error (task,
1219                                IDE_RUNTIME_ERROR,
1220                                IDE_RUNTIME_ERROR_TARGET_NOT_FOUND,
1221                                _("Failed to locate a build target"));
1222 
1223   IDE_EXIT;
1224 }
1225 
1226 /**
1227  * ide_run_manager_discover_default_target_finish:
1228  *
1229  * Returns: (transfer full): An #IdeBuildTarget if successful; otherwise %NULL
1230  *   and @error is set.
1231  *
1232  * Since: 3.32
1233  */
1234 IdeBuildTarget *
ide_run_manager_discover_default_target_finish(IdeRunManager * self,GAsyncResult * result,GError ** error)1235 ide_run_manager_discover_default_target_finish (IdeRunManager  *self,
1236                                                 GAsyncResult   *result,
1237                                                 GError        **error)
1238 {
1239   IdeBuildTarget *ret;
1240 
1241   IDE_ENTRY;
1242 
1243   g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
1244   g_return_val_if_fail (IDE_IS_TASK (result), NULL);
1245 
1246   ret = ide_task_propagate_pointer (IDE_TASK (result), error);
1247 
1248   IDE_RETURN (ret);
1249 }
1250 
1251 const GList *
_ide_run_manager_get_handlers(IdeRunManager * self)1252 _ide_run_manager_get_handlers (IdeRunManager *self)
1253 {
1254   g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
1255 
1256   return self->handlers;
1257 }
1258 
1259 const gchar *
ide_run_manager_get_handler(IdeRunManager * self)1260 ide_run_manager_get_handler (IdeRunManager *self)
1261 {
1262   g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
1263 
1264   if (self->handler != NULL)
1265     return self->handler->id;
1266 
1267   return NULL;
1268 }
1269 
1270 static void
ide_run_manager_run_action_cb(GObject * object,GAsyncResult * result,gpointer user_data)1271 ide_run_manager_run_action_cb (GObject      *object,
1272                                GAsyncResult *result,
1273                                gpointer      user_data)
1274 {
1275   IdeRunManager *self = (IdeRunManager *)object;
1276   IdeContext *context;
1277   g_autoptr(GError) error = NULL;
1278 
1279   g_assert (IDE_IS_RUN_MANAGER (self));
1280   g_assert (G_IS_ASYNC_RESULT (result));
1281 
1282   context = ide_object_get_context (IDE_OBJECT (self));
1283 
1284   /* Propagate the error to the context */
1285   if (!ide_run_manager_run_finish (self, result, &error))
1286     ide_context_warning (context, "%s", error->message);
1287 }
1288 
1289 static void
ide_run_manager_actions_run(IdeRunManager * self,GVariant * param)1290 ide_run_manager_actions_run (IdeRunManager *self,
1291                              GVariant      *param)
1292 {
1293   IDE_ENTRY;
1294 
1295   g_assert (IDE_IS_RUN_MANAGER (self));
1296 
1297   ide_run_manager_run_async (self,
1298                              NULL,
1299                              NULL,
1300                              ide_run_manager_run_action_cb,
1301                              NULL);
1302 
1303   IDE_EXIT;
1304 }
1305 
1306 static void
ide_run_manager_actions_run_with_handler(IdeRunManager * self,GVariant * param)1307 ide_run_manager_actions_run_with_handler (IdeRunManager *self,
1308                                           GVariant      *param)
1309 {
1310   const gchar *handler = NULL;
1311   g_autoptr(GVariant) sunk = NULL;
1312 
1313   IDE_ENTRY;
1314 
1315   g_assert (IDE_IS_RUN_MANAGER (self));
1316 
1317   if (param != NULL)
1318   {
1319     handler = g_variant_get_string (param, NULL);
1320     if (g_variant_is_floating (param))
1321       sunk = g_variant_ref_sink (param);
1322   }
1323 
1324   /* Use specified handler, if provided */
1325   if (!ide_str_empty0 (handler))
1326     ide_run_manager_set_handler (self, handler);
1327 
1328   ide_run_manager_run_async (self,
1329                              NULL,
1330                              NULL,
1331                              ide_run_manager_run_action_cb,
1332                              NULL);
1333 
1334   IDE_EXIT;
1335 }
1336 
1337 static void
ide_run_manager_actions_stop(IdeRunManager * self,GVariant * param)1338 ide_run_manager_actions_stop (IdeRunManager *self,
1339                               GVariant      *param)
1340 {
1341   IDE_ENTRY;
1342 
1343   g_assert (IDE_IS_RUN_MANAGER (self));
1344 
1345   ide_run_manager_cancel (self);
1346 
1347   IDE_EXIT;
1348 }
1349 
1350 static void
ide_run_manager_init(IdeRunManager * self)1351 ide_run_manager_init (IdeRunManager *self)
1352 {
1353   self->cancellable = g_cancellable_new ();
1354 
1355   ide_run_manager_add_handler (self,
1356                                "run",
1357                                _("Run"),
1358                                "builder-run-start-symbolic",
1359                                "<primary>F5",
1360                                NULL,
1361                                NULL,
1362                                NULL);
1363 }
1364 
1365 void
_ide_run_manager_drop_caches(IdeRunManager * self)1366 _ide_run_manager_drop_caches (IdeRunManager *self)
1367 {
1368   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
1369 
1370   self->last_change_seq = 0;
1371 }
1372