1 /* ide-build-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-build-manager"
22 
23 #include "config.h"
24 
25 #include <dazzle.h>
26 #include <glib/gi18n.h>
27 #include <libide-code.h>
28 #include <libide-threading.h>
29 #include <libide-vcs.h>
30 
31 #include "ide-build-manager.h"
32 #include "ide-build-private.h"
33 #include "ide-config-manager.h"
34 #include "ide-config.h"
35 #include "ide-device-info.h"
36 #include "ide-device-manager.h"
37 #include "ide-device.h"
38 #include "ide-foundry-compat.h"
39 #include "ide-pipeline.h"
40 #include "ide-run-manager.h"
41 #include "ide-runtime-manager.h"
42 #include "ide-runtime-private.h"
43 #include "ide-runtime.h"
44 #include "ide-toolchain-manager.h"
45 #include "ide-toolchain-private.h"
46 
47 /**
48  * SECTION:ide-build-manager
49  * @title: IdeBuildManager
50  * @short_description: Manages the active build configuration and pipeline
51  *
52  * The #IdeBuildManager is responsible for managing the active build pipeline
53  * as well as providing common high-level actions to plugins.
54  *
55  * You can use various async operations such as
56  * ide_build_manager_build_async(), ide_build_manager_clean_async(), or
57  * ide_build_manager_rebuild_async() to build, clean, and rebuild respectively
58  * without needing to track the build pipeline.
59  *
60  * The #IdePipeline is used to specify how and when build operations
61  * should occur. Plugins attach build stages to the pipeline to perform
62  * build actions.
63  *
64  * Since: 3.32
65  */
66 
67 struct _IdeBuildManager
68 {
69   IdeObject         parent_instance;
70 
71   GCancellable     *cancellable;
72 
73   IdePipeline      *pipeline;
74   GDateTime        *last_build_time;
75   DzlSignalGroup   *pipeline_signals;
76 
77   gchar            *branch_name;
78 
79   GTimer           *running_time;
80 
81   guint             diagnostic_count;
82   guint             error_count;
83   guint             warning_count;
84 
85   guint             timer_source;
86 
87   guint             started : 1;
88   guint             can_build : 1;
89   guint             can_export : 1;
90   guint             building : 1;
91   guint             needs_rediagnose : 1;
92   guint             has_configured : 1;
93 };
94 
95 static void initable_iface_init              (GInitableIface  *iface);
96 static void ide_build_manager_set_can_build  (IdeBuildManager *self,
97                                               gboolean         can_build);
98 static void ide_build_manager_action_build   (IdeBuildManager *self,
99                                               GVariant        *param);
100 static void ide_build_manager_action_rebuild (IdeBuildManager *self,
101                                               GVariant        *param);
102 static void ide_build_manager_action_cancel  (IdeBuildManager *self,
103                                               GVariant        *param);
104 static void ide_build_manager_action_clean   (IdeBuildManager *self,
105                                               GVariant        *param);
106 static void ide_build_manager_action_export  (IdeBuildManager *self,
107                                               GVariant        *param);
108 static void ide_build_manager_action_install (IdeBuildManager *self,
109                                               GVariant        *param);
110 
111 DZL_DEFINE_ACTION_GROUP (IdeBuildManager, ide_build_manager, {
112   { "build", ide_build_manager_action_build },
113   { "cancel", ide_build_manager_action_cancel },
114   { "clean", ide_build_manager_action_clean },
115   { "export", ide_build_manager_action_export },
116   { "install", ide_build_manager_action_install },
117   { "rebuild", ide_build_manager_action_rebuild },
118 })
119 
120 G_DEFINE_TYPE_EXTENDED (IdeBuildManager, ide_build_manager, IDE_TYPE_OBJECT, G_TYPE_FLAG_FINAL,
121                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
122                         G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
123                                                ide_build_manager_init_action_group))
124 
125 enum {
126   PROP_0,
127   PROP_BUSY,
128   PROP_CAN_BUILD,
129   PROP_ERROR_COUNT,
130   PROP_HAS_DIAGNOSTICS,
131   PROP_LAST_BUILD_TIME,
132   PROP_MESSAGE,
133   PROP_PIPELINE,
134   PROP_RUNNING_TIME,
135   PROP_WARNING_COUNT,
136   N_PROPS
137 };
138 
139 enum {
140   BUILD_STARTED,
141   BUILD_FINISHED,
142   BUILD_FAILED,
143   N_SIGNALS
144 };
145 
146 static GParamSpec *properties [N_PROPS];
147 static guint signals [N_SIGNALS];
148 
149 static gboolean
timer_callback(gpointer data)150 timer_callback (gpointer data)
151 {
152   IdeBuildManager *self = data;
153 
154   g_assert (IDE_IS_BUILD_MANAGER (self));
155 
156   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RUNNING_TIME]);
157 
158   return G_SOURCE_CONTINUE;
159 }
160 
161 static void
ide_build_manager_start_timer(IdeBuildManager * self)162 ide_build_manager_start_timer (IdeBuildManager *self)
163 {
164   IDE_ENTRY;
165 
166   g_assert (IDE_IS_BUILD_MANAGER (self));
167   g_assert (self->timer_source == 0);
168 
169   if (self->running_time != NULL)
170     g_timer_start (self->running_time);
171   else
172     self->running_time = g_timer_new ();
173 
174   /*
175    * We use the DzlFrameSource for our timer callback because we only want to
176    * update at a rate somewhat close to a typical monitor refresh rate.
177    * Additionally, we want to handle drift (which that source does) so that we
178    * don't constantly fall behind.
179    */
180   self->timer_source = g_timeout_add_seconds (1, timer_callback, self);
181 
182   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RUNNING_TIME]);
183 
184   IDE_EXIT;
185 }
186 
187 static void
ide_build_manager_stop_timer(IdeBuildManager * self)188 ide_build_manager_stop_timer (IdeBuildManager *self)
189 {
190   IDE_ENTRY;
191 
192   g_assert (IDE_IS_BUILD_MANAGER (self));
193 
194   dzl_clear_source (&self->timer_source);
195 
196   if (self->running_time != NULL)
197     {
198       g_timer_stop (self->running_time);
199       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RUNNING_TIME]);
200     }
201 
202   IDE_EXIT;
203 }
204 
205 static void
ide_build_manager_reset_info(IdeBuildManager * self)206 ide_build_manager_reset_info (IdeBuildManager *self)
207 {
208   g_assert (IDE_IS_BUILD_MANAGER (self));
209 
210   g_clear_pointer (&self->last_build_time, g_date_time_unref);
211   self->last_build_time = g_date_time_new_now_local ();
212 
213   self->diagnostic_count = 0;
214   self->warning_count = 0;
215   self->error_count = 0;
216 
217   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ERROR_COUNT]);
218   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_DIAGNOSTICS]);
219   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LAST_BUILD_TIME]);
220   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RUNNING_TIME]);
221   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_WARNING_COUNT]);
222 }
223 
224 static void
ide_build_manager_handle_diagnostic(IdeBuildManager * self,IdeDiagnostic * diagnostic,IdePipeline * pipeline)225 ide_build_manager_handle_diagnostic (IdeBuildManager *self,
226                                      IdeDiagnostic   *diagnostic,
227                                      IdePipeline     *pipeline)
228 {
229   IdeDiagnosticSeverity severity;
230 
231   IDE_ENTRY;
232 
233   g_assert (IDE_IS_BUILD_MANAGER (self));
234   g_assert (diagnostic != NULL);
235   g_assert (IDE_IS_PIPELINE (pipeline));
236 
237   self->diagnostic_count++;
238   if (self->diagnostic_count == 1)
239     g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_DIAGNOSTICS]);
240 
241   severity = ide_diagnostic_get_severity (diagnostic);
242 
243   if (severity == IDE_DIAGNOSTIC_WARNING)
244     {
245       self->warning_count++;
246       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_WARNING_COUNT]);
247     }
248   else if (severity == IDE_DIAGNOSTIC_ERROR || severity == IDE_DIAGNOSTIC_FATAL)
249     {
250       self->error_count++;
251       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ERROR_COUNT]);
252     }
253 
254   IDE_EXIT;
255 }
256 
257 static void
ide_build_manager_update_action_enabled(IdeBuildManager * self)258 ide_build_manager_update_action_enabled (IdeBuildManager *self)
259 {
260   gboolean busy;
261   gboolean can_build;
262   gboolean can_export;
263 
264   g_assert (IDE_IS_BUILD_MANAGER (self));
265 
266   busy = ide_build_manager_get_busy (self);
267   can_build = ide_build_manager_get_can_build (self);
268   can_export = self->pipeline ? ide_pipeline_get_can_export (self->pipeline) : FALSE;
269 
270   ide_build_manager_set_action_enabled (self, "build", !busy && can_build);
271   ide_build_manager_set_action_enabled (self, "cancel", busy);
272   ide_build_manager_set_action_enabled (self, "clean", !busy && can_build);
273   ide_build_manager_set_action_enabled (self, "export", !busy && can_build && can_export);
274   ide_build_manager_set_action_enabled (self, "install", !busy && can_build);
275   ide_build_manager_set_action_enabled (self, "rebuild", !busy && can_build);
276 
277   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
278 }
279 
280 static void
ide_build_manager_notify_busy(IdeBuildManager * self,GParamSpec * pspec,IdePipeline * pipeline)281 ide_build_manager_notify_busy (IdeBuildManager *self,
282                                GParamSpec      *pspec,
283                                IdePipeline     *pipeline)
284 {
285   IDE_ENTRY;
286 
287   g_assert (IDE_IS_BUILD_MANAGER (self));
288   g_assert (G_IS_PARAM_SPEC (pspec));
289   g_assert (IDE_IS_PIPELINE (pipeline));
290 
291   ide_build_manager_update_action_enabled (self);
292 
293   IDE_EXIT;
294 }
295 
296 static void
ide_build_manager_notify_message(IdeBuildManager * self,GParamSpec * pspec,IdePipeline * pipeline)297 ide_build_manager_notify_message (IdeBuildManager *self,
298                                   GParamSpec      *pspec,
299                                   IdePipeline     *pipeline)
300 {
301   IDE_ENTRY;
302 
303   g_assert (IDE_IS_BUILD_MANAGER (self));
304   g_assert (G_IS_PARAM_SPEC (pspec));
305   g_assert (IDE_IS_PIPELINE (pipeline));
306 
307   if (pipeline == self->pipeline)
308     g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MESSAGE]);
309 
310   IDE_EXIT;
311 }
312 
313 static void
ide_build_manager_pipeline_started(IdeBuildManager * self,IdePipelinePhase phase,IdePipeline * pipeline)314 ide_build_manager_pipeline_started (IdeBuildManager  *self,
315                                     IdePipelinePhase  phase,
316                                     IdePipeline      *pipeline)
317 {
318   IDE_ENTRY;
319 
320   g_assert (IDE_IS_BUILD_MANAGER (self));
321   g_assert (IDE_IS_PIPELINE (pipeline));
322 
323   self->building = TRUE;
324 
325   g_signal_emit (self, signals [BUILD_STARTED], 0, pipeline);
326 
327   IDE_EXIT;
328 }
329 
330 static void
ide_build_manager_pipeline_finished(IdeBuildManager * self,gboolean failed,IdePipeline * pipeline)331 ide_build_manager_pipeline_finished (IdeBuildManager *self,
332                                      gboolean         failed,
333                                      IdePipeline     *pipeline)
334 {
335   IDE_ENTRY;
336 
337   g_assert (IDE_IS_BUILD_MANAGER (self));
338   g_assert (IDE_IS_PIPELINE (pipeline));
339 
340   self->building = FALSE;
341 
342   if (failed)
343     g_signal_emit (self, signals [BUILD_FAILED], 0, pipeline);
344   else
345     g_signal_emit (self, signals [BUILD_FINISHED], 0, pipeline);
346 
347   IDE_EXIT;
348 }
349 
350 static void
ide_build_manager_ensure_toolchain_cb(GObject * object,GAsyncResult * result,gpointer user_data)351 ide_build_manager_ensure_toolchain_cb (GObject      *object,
352                                        GAsyncResult *result,
353                                        gpointer      user_data)
354 {
355   IdeToolchainManager *toolchain_manager = (IdeToolchainManager *)object;
356   g_autoptr(GError) error = NULL;
357   g_autoptr(IdeTask) task = user_data;
358   IdePipeline *pipeline;
359   IdeBuildManager *self;
360   GCancellable *cancellable;
361 
362   IDE_ENTRY;
363 
364   g_assert (IDE_IS_TOOLCHAIN_MANAGER (toolchain_manager));
365   g_assert (G_IS_ASYNC_RESULT (result));
366   g_assert (IDE_IS_TASK (task));
367 
368   self = ide_task_get_source_object (task);
369   pipeline = ide_task_get_task_data (task);
370 
371   g_assert (IDE_IS_BUILD_MANAGER (self));
372   g_assert (IDE_IS_PIPELINE (pipeline));
373 
374   if (!_ide_toolchain_manager_prepare_finish (toolchain_manager, result, &error))
375     {
376       g_message ("Failed to prepare toolchain: %s", error->message);
377       IDE_GOTO (failure);
378     }
379 
380   if (pipeline != self->pipeline)
381     {
382       IDE_TRACE_MSG ("pipeline is no longer active, ignoring");
383       IDE_GOTO (failure);
384     }
385 
386   if (ide_task_return_error_if_cancelled (task))
387     IDE_GOTO (failure);
388 
389   cancellable = ide_task_get_cancellable (task);
390 
391   /* This will cause plugins to load on the pipeline. */
392   if (!g_initable_init (G_INITABLE (pipeline), cancellable, &error))
393     {
394       /* translators: %s is replaced with the error message */
395       ide_object_warning (self,
396                           _("Failed to initialize build pipeline: %s"),
397                           error->message);
398       IDE_GOTO (failure);
399     }
400 
401   ide_build_manager_set_can_build (self, TRUE);
402 
403   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PIPELINE]);
404 
405   ide_task_return_boolean (task, TRUE);
406   IDE_EXIT;
407 
408 failure:
409 
410   if (error != NULL)
411     ide_task_return_error (task, g_steal_pointer (&error));
412   else
413     ide_task_return_new_error (task,
414                                G_IO_ERROR,
415                                G_IO_ERROR_FAILED,
416                                "Failed to setup build pipeline");
417 
418   IDE_EXIT;
419 }
420 
421 static void
ide_build_manager_ensure_runtime_cb(GObject * object,GAsyncResult * result,gpointer user_data)422 ide_build_manager_ensure_runtime_cb (GObject      *object,
423                                      GAsyncResult *result,
424                                      gpointer      user_data)
425 {
426   IdeRuntimeManager *runtime_manager = (IdeRuntimeManager *)object;
427   g_autoptr(GError) error = NULL;
428   g_autoptr(IdeTask) task = user_data;
429   IdePipeline *pipeline;
430   IdeBuildManager *self;
431   IdeToolchainManager *toolchain_manager;
432   IdeContext *context;
433 
434   IDE_ENTRY;
435 
436   g_assert (IDE_IS_RUNTIME_MANAGER (runtime_manager));
437   g_assert (G_IS_ASYNC_RESULT (result));
438   g_assert (IDE_IS_TASK (task));
439 
440   self = ide_task_get_source_object (task);
441   pipeline = ide_task_get_task_data (task);
442 
443   g_assert (IDE_IS_BUILD_MANAGER (self));
444   g_assert (IDE_IS_PIPELINE (pipeline));
445 
446   if (!_ide_runtime_manager_prepare_finish (runtime_manager, result, &error))
447     {
448       if (error != NULL)
449         g_message ("Failed to prepare runtime: %s", error->message);
450       IDE_GOTO (failure);
451     }
452 
453   if (pipeline != self->pipeline)
454     {
455       IDE_TRACE_MSG ("pipeline is no longer active, ignoring");
456       IDE_GOTO (failure);
457     }
458 
459   if (ide_task_return_error_if_cancelled (task))
460     IDE_GOTO (failure);
461 
462   context = ide_object_get_context (IDE_OBJECT (pipeline));
463   g_assert (IDE_IS_CONTEXT (context));
464 
465   toolchain_manager = ide_toolchain_manager_from_context (context);
466   g_assert (IDE_IS_TOOLCHAIN_MANAGER (toolchain_manager));
467 
468   _ide_toolchain_manager_prepare_async (toolchain_manager,
469                                         pipeline,
470                                         ide_task_get_cancellable (task),
471                                         ide_build_manager_ensure_toolchain_cb,
472                                         g_object_ref (task));
473 
474   IDE_EXIT;
475 
476 failure:
477 
478   if (error != NULL)
479     ide_task_return_error (task, g_steal_pointer (&error));
480   else
481     ide_task_return_new_error (task,
482                                G_IO_ERROR,
483                                G_IO_ERROR_FAILED,
484                                "Failed to setup build pipeline");
485 
486   IDE_EXIT;
487 }
488 
489 static void
ide_build_manager_device_get_info_cb(GObject * object,GAsyncResult * result,gpointer user_data)490 ide_build_manager_device_get_info_cb (GObject      *object,
491                                       GAsyncResult *result,
492                                       gpointer      user_data)
493 {
494   IdeDevice *device = (IdeDevice *)object;
495   g_autoptr(IdeDeviceInfo) info = NULL;
496   g_autoptr(GError) error = NULL;
497   g_autoptr(IdeTask) task = user_data;
498   IdeRuntimeManager *runtime_manager;
499   IdePipeline *pipeline;
500   IdeContext *context;
501 
502   IDE_ENTRY;
503 
504   g_assert (IDE_IS_DEVICE (device));
505   g_assert (G_IS_ASYNC_RESULT (result));
506   g_assert (IDE_IS_TASK (task));
507 
508   pipeline = ide_task_get_task_data (task);
509   g_assert (IDE_IS_PIPELINE (pipeline));
510 
511   if (ide_task_return_error_if_cancelled (task))
512     IDE_EXIT;
513 
514   if (!(context = ide_object_get_context (IDE_OBJECT (pipeline))) ||
515       !(runtime_manager = ide_runtime_manager_from_context (context)))
516     {
517       ide_task_return_new_error (task,
518                                  G_IO_ERROR,
519                                  G_IO_ERROR_CANCELLED,
520                                  "Device was destroyed");
521       IDE_EXIT;
522     }
523 
524   if (!(info = ide_device_get_info_finish (device, result, &error)))
525     {
526       ide_context_warning (context,
527                            _("Failed to get device information: %s"),
528                            error->message);
529       ide_task_return_error (task, g_steal_pointer (&error));
530       IDE_EXIT;
531     }
532 
533   IDE_TRACE_MSG (" Device Kind = %d", ide_device_info_get_kind (info));
534   IDE_TRACE_MSG (" Device Triplet = %s",
535                  ide_triplet_get_full_name (ide_device_info_get_host_triplet (info)));
536 
537   _ide_pipeline_check_toolchain (pipeline, info);
538 
539   _ide_runtime_manager_prepare_async (runtime_manager,
540                                       pipeline,
541                                       ide_task_get_cancellable (task),
542                                       ide_build_manager_ensure_runtime_cb,
543                                       g_object_ref (task));
544 
545   IDE_EXIT;
546 }
547 
548 static void
ide_build_manager_invalidate_pipeline(IdeBuildManager * self)549 ide_build_manager_invalidate_pipeline (IdeBuildManager *self)
550 {
551   g_autoptr(IdeTask) task = NULL;
552   IdeConfigManager *config_manager;
553   IdeDeviceManager *device_manager;
554   IdeRunManager *run_manager;
555   IdeConfig *config;
556   IdeContext *context;
557   IdeDevice *device;
558 
559   IDE_ENTRY;
560 
561   g_assert (IDE_IS_BUILD_MANAGER (self));
562 
563   context = ide_object_get_context (IDE_OBJECT (self));
564 
565   IDE_TRACE_MSG ("Reloading pipeline due to configuration change");
566 
567   /*
568    * If we are currently building, we need to synthesize the failure
569    * of that build and re-setup the pipeline.
570    */
571   if (self->building)
572     {
573       g_assert (self->pipeline != NULL);
574 
575       self->building = FALSE;
576       dzl_clear_source (&self->timer_source);
577       g_signal_emit (self, signals [BUILD_FAILED], 0, self->pipeline);
578     }
579 
580   /*
581    * Clear any cached build targets from the run manager.
582    */
583   run_manager = ide_run_manager_from_context (context);
584   ide_run_manager_set_build_target (run_manager, NULL);
585 
586   /*
587    * Cancel and clear our previous pipeline and associated components
588    * as they are not invalide.
589    */
590   ide_build_manager_cancel (self);
591 
592   ide_clear_and_destroy_object (&self->pipeline);
593 
594   g_clear_pointer (&self->running_time, g_timer_destroy);
595 
596   self->diagnostic_count = 0;
597   self->error_count = 0;
598   self->warning_count = 0;
599 
600   /* Don't setup anything new if we're in shutdown or we haven't
601    * been told we are allowed to start.
602    */
603   if (ide_object_in_destruction (IDE_OBJECT (context)) || !self->started)
604     IDE_EXIT;
605 
606   config_manager = ide_config_manager_from_context (context);
607   device_manager = ide_device_manager_from_context (context);
608 
609   config = ide_config_manager_get_current (config_manager);
610   device = ide_device_manager_get_device (device_manager);
611 
612   /*
613    * We want to set the pipeline before connecting things using the GInitable
614    * interface so that we can access the builddir from
615    * IdeRuntime.create_launcher() during pipeline addin initialization.
616    *
617    * We will delay the initialization until after the we have ensured the
618    * runtime is available (possibly installing it).
619    */
620   ide_build_manager_set_can_build (self, FALSE);
621   self->pipeline = g_object_new (IDE_TYPE_PIPELINE,
622                                  "config", config,
623                                  "device", device,
624                                  NULL);
625   ide_object_append (IDE_OBJECT (self), IDE_OBJECT (self->pipeline));
626   dzl_signal_group_set_target (self->pipeline_signals, self->pipeline);
627 
628   /*
629    * Create a task to manage our async pipeline initialization state.
630    */
631   task = ide_task_new (self, self->cancellable, NULL, NULL);
632   ide_task_set_task_data (task, g_object_ref (self->pipeline), g_object_unref);
633   ide_task_set_priority (task, G_PRIORITY_LOW);
634 
635   /*
636    * Next, we need to get information on the build device, which may require
637    * connecting to it. So we will query that information (so we can get
638    * arch/kernel/system too). We might need that when bootstrapping the
639    * runtime (if it's missing) among other things.
640    */
641   ide_device_get_info_async (device,
642                              self->cancellable,
643                              ide_build_manager_device_get_info_cb,
644                              g_steal_pointer (&task));
645 
646   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ERROR_COUNT]);
647   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_DIAGNOSTICS]);
648   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LAST_BUILD_TIME]);
649   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MESSAGE]);
650   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RUNNING_TIME]);
651   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_WARNING_COUNT]);
652 
653   IDE_EXIT;
654 }
655 
656 static void
ide_build_manager_vcs_changed(IdeBuildManager * self,IdeVcs * vcs)657 ide_build_manager_vcs_changed (IdeBuildManager *self,
658                                IdeVcs          *vcs)
659 {
660   g_autofree gchar *branch_name = NULL;
661 
662   g_assert (IDE_IS_BUILD_MANAGER (self));
663   g_assert (IDE_IS_VCS (vcs));
664 
665   /* Only invalidate the pipeline if they switched branches. Ignore things
666    * like opening up `git gui` or other things that could touch the index
667    * without really changing things out from underneath us.
668    */
669 
670   branch_name = ide_vcs_get_branch_name (vcs);
671 
672   if (!ide_str_equal0 (branch_name, self->branch_name))
673     {
674       g_free (self->branch_name);
675       self->branch_name = g_strdup (branch_name);
676       ide_build_manager_invalidate_pipeline (self);
677     }
678 }
679 
680 static gboolean
initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)681 initable_init (GInitable     *initable,
682                GCancellable  *cancellable,
683                GError       **error)
684 {
685   IdeBuildManager *self = (IdeBuildManager *)initable;
686   IdeConfigManager *config_manager;
687   IdeDeviceManager *device_manager;
688   IdeContext *context;
689   IdeVcs *vcs;
690 
691   IDE_ENTRY;
692 
693   g_assert (IDE_IS_BUILD_MANAGER (self));
694   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
695 
696   context = ide_object_get_context (IDE_OBJECT (self));
697   config_manager = ide_config_manager_from_context (context);
698   device_manager = ide_device_manager_from_context (context);
699   vcs = ide_vcs_from_context (context);
700 
701   self->branch_name = ide_vcs_get_branch_name (vcs);
702 
703   g_signal_connect_object (config_manager,
704                            "invalidate",
705                            G_CALLBACK (ide_build_manager_invalidate_pipeline),
706                            self,
707                            G_CONNECT_SWAPPED);
708 
709   g_signal_connect_object (device_manager,
710                            "notify::device",
711                            G_CALLBACK (ide_build_manager_invalidate_pipeline),
712                            self,
713                            G_CONNECT_SWAPPED);
714 
715   g_signal_connect_object (vcs,
716                            "changed",
717                            G_CALLBACK (ide_build_manager_vcs_changed),
718                            self,
719                            G_CONNECT_SWAPPED);
720 
721   ide_build_manager_invalidate_pipeline (self);
722 
723   IDE_RETURN (TRUE);
724 }
725 
726 static void
ide_build_manager_real_build_started(IdeBuildManager * self,IdePipeline * pipeline)727 ide_build_manager_real_build_started (IdeBuildManager *self,
728                                       IdePipeline     *pipeline)
729 {
730   IdePipelinePhase phase;
731 
732   g_assert (IDE_IS_BUILD_MANAGER (self));
733   g_assert (IDE_IS_PIPELINE (pipeline));
734 
735   ide_build_manager_start_timer (self);
736 
737   /*
738    * When the build completes, we may want to update diagnostics for
739    * files that are open. But we only want to do this if we are reaching
740    * configure for the first time, or performing a real build.
741    */
742 
743   phase = ide_pipeline_get_requested_phase (pipeline);
744   g_assert ((phase & IDE_PIPELINE_PHASE_MASK) == phase);
745 
746   if (phase == IDE_PIPELINE_PHASE_BUILD ||
747       (phase == IDE_PIPELINE_PHASE_CONFIGURE && !self->has_configured))
748     {
749       self->needs_rediagnose = TRUE;
750       self->has_configured = TRUE;
751     }
752 }
753 
754 static void
ide_build_manager_real_build_failed(IdeBuildManager * self,IdePipeline * pipeline)755 ide_build_manager_real_build_failed (IdeBuildManager *self,
756                                      IdePipeline     *pipeline)
757 {
758   g_assert (IDE_IS_BUILD_MANAGER (self));
759   g_assert (IDE_IS_PIPELINE (pipeline));
760 
761   ide_build_manager_stop_timer (self);
762 }
763 
764 static void
ide_build_manager_real_build_finished(IdeBuildManager * self,IdePipeline * pipeline)765 ide_build_manager_real_build_finished (IdeBuildManager *self,
766                                        IdePipeline     *pipeline)
767 {
768   IdeDiagnosticsManager *diagnostics;
769   IdeBufferManager *bufmgr;
770   IdeContext *context;
771   guint n_items;
772 
773   g_assert (IDE_IS_BUILD_MANAGER (self));
774   g_assert (IDE_IS_PIPELINE (pipeline));
775 
776   ide_build_manager_stop_timer (self);
777 
778   /*
779    * If this was not a full build (such as advancing to just the configure
780    * phase or so), then there is nothing more to do.
781    */
782   if (!self->needs_rediagnose)
783     return;
784 
785   /*
786    * We had a successful build, so lets notify the build manager to reload
787    * dianostics on loaded buffers so the user doesn't have to make a change
788    * to force the update.
789    */
790 
791   context = ide_object_get_context (IDE_OBJECT (self));
792   diagnostics = ide_diagnostics_manager_from_context (context);
793   bufmgr = ide_buffer_manager_from_context (context);
794   n_items = g_list_model_get_n_items (G_LIST_MODEL (bufmgr));
795 
796   for (guint i = 0; i < n_items; i++)
797     {
798       g_autoptr(IdeBuffer) buffer = g_list_model_get_item (G_LIST_MODEL (bufmgr), i);
799 
800       ide_diagnostics_manager_rediagnose (diagnostics, buffer);
801     }
802 
803   self->needs_rediagnose = FALSE;
804 }
805 
806 static void
initable_iface_init(GInitableIface * iface)807 initable_iface_init (GInitableIface *iface)
808 {
809   iface->init = initable_init;
810 }
811 
812 static void
ide_build_manager_finalize(GObject * object)813 ide_build_manager_finalize (GObject *object)
814 {
815   IdeBuildManager *self = (IdeBuildManager *)object;
816 
817   ide_clear_and_destroy_object (&self->pipeline);
818   g_clear_object (&self->pipeline_signals);
819   g_clear_object (&self->cancellable);
820   g_clear_pointer (&self->last_build_time, g_date_time_unref);
821   g_clear_pointer (&self->running_time, g_timer_destroy);
822   g_clear_pointer (&self->branch_name, g_free);
823 
824   dzl_clear_source (&self->timer_source);
825 
826   G_OBJECT_CLASS (ide_build_manager_parent_class)->finalize (object);
827 }
828 
829 static void
ide_build_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)830 ide_build_manager_get_property (GObject    *object,
831                                 guint       prop_id,
832                                 GValue     *value,
833                                 GParamSpec *pspec)
834 {
835   IdeBuildManager *self = IDE_BUILD_MANAGER (object);
836 
837   switch (prop_id)
838     {
839     case PROP_BUSY:
840       g_value_set_boolean (value, ide_build_manager_get_busy (self));
841       break;
842 
843     case PROP_CAN_BUILD:
844       g_value_set_boolean (value, ide_build_manager_get_can_build (self));
845       break;
846 
847     case PROP_MESSAGE:
848       g_value_take_string (value, ide_build_manager_get_message (self));
849       break;
850 
851     case PROP_LAST_BUILD_TIME:
852       g_value_set_boxed (value, ide_build_manager_get_last_build_time (self));
853       break;
854 
855     case PROP_RUNNING_TIME:
856       g_value_set_int64 (value, ide_build_manager_get_running_time (self));
857       break;
858 
859     case PROP_HAS_DIAGNOSTICS:
860       g_value_set_boolean (value, self->diagnostic_count > 0);
861       break;
862 
863     case PROP_ERROR_COUNT:
864       g_value_set_uint (value, self->error_count);
865       break;
866 
867     case PROP_WARNING_COUNT:
868       g_value_set_uint (value, self->warning_count);
869       break;
870 
871     case PROP_PIPELINE:
872       g_value_set_object (value, self->pipeline);
873       break;
874 
875     default:
876       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
877     }
878 }
879 
880 static void
ide_build_manager_class_init(IdeBuildManagerClass * klass)881 ide_build_manager_class_init (IdeBuildManagerClass *klass)
882 {
883   GObjectClass *object_class = G_OBJECT_CLASS (klass);
884 
885   object_class->finalize = ide_build_manager_finalize;
886   object_class->get_property = ide_build_manager_get_property;
887 
888   /**
889    * IdeBuildManager:can-build:
890    *
891    * Gets if the build manager can queue a build request.
892    *
893    * This might be false if the required runtime is not available or other
894    * errors in setting up the build pipeline.
895    *
896    * Since: 3.32
897    */
898   properties [PROP_CAN_BUILD] =
899     g_param_spec_boolean ("can-build",
900                           "Can Build",
901                           "If the manager can queue a build",
902                           FALSE,
903                           (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
904 
905   /**
906    * IdeBuildManager:busy:
907    *
908    * The "busy" property indicates if there is currently a build
909    * executing. This can be bound to UI elements to display to the
910    * user that a build is active (and therefore other builds cannot
911    * be activated at the moment).
912    *
913    * Since: 3.32
914    */
915   properties [PROP_BUSY] =
916     g_param_spec_boolean ("busy",
917                           "Busy",
918                           "If a build is actively executing",
919                           FALSE,
920                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
921 
922   /**
923    * IdeBuildManager:error-count:
924    *
925    * The number of errors discovered during the build process.
926    *
927    * Since: 3.32
928    */
929   properties [PROP_ERROR_COUNT] =
930     g_param_spec_uint ("error-count",
931                        "Error Count",
932                        "The number of errors that have been seen in the current build",
933                        0, G_MAXUINT, 0,
934                        (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
935 
936   /**
937    * IdeBuildManager:has-diagnostics:
938    *
939    * The "has-diagnostics" property indicates that there have been
940    * diagnostics found during the last execution of the build pipeline.
941    *
942    * Since: 3.32
943    */
944   properties [PROP_HAS_DIAGNOSTICS] =
945     g_param_spec_boolean ("has-diagnostics",
946                           "Has Diagnostics",
947                           "Has Diagnostics",
948                           FALSE,
949                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
950 
951   /**
952    * IdeBuildManager:last-build-time:
953    *
954    * The "last-build-time" property contains a #GDateTime of the time
955    * the last build request was submitted.
956    *
957    * Since: 3.32
958    */
959   properties [PROP_LAST_BUILD_TIME] =
960     g_param_spec_boxed ("last-build-time",
961                         "Last Build Time",
962                         "The time of the last build request",
963                         G_TYPE_DATE_TIME,
964                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
965 
966   /**
967    * IdeBuildManager:message:
968    *
969    * The "message" property contains a string message describing
970    * the current state of the build process. This may be bound to
971    * UI elements to notify the user of the buid progress.
972    *
973    * Since: 3.32
974    */
975   properties [PROP_MESSAGE] =
976     g_param_spec_string ("message",
977                          "Message",
978                          "The current build message",
979                          NULL,
980                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
981 
982   /**
983    * IdeBuildManager:pipeline:
984    *
985    * The "pipeline" property is the build pipeline that the build manager
986    * is currently managing.
987    *
988    * Since: 3.32
989    */
990   properties [PROP_PIPELINE] =
991     g_param_spec_object ("pipeline",
992                          "Pipeline",
993                          "The build pipeline",
994                          IDE_TYPE_PIPELINE,
995                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
996 
997   /**
998    * IdeBuildManager:running-time:
999    *
1000    * The "running-time" property can be bound by UI elements that
1001    * want to track how long the current build has taken. g_object_notify()
1002    * is called on a regular interval during the build so that the UI
1003    * elements may automatically update.
1004    *
1005    * The value of this property is a #GTimeSpan, which are 64-bit signed
1006    * integers with microsecond precision. See %G_USEC_PER_SEC for a constant
1007    * to tranform this to seconds.
1008    *
1009    * Since: 3.32
1010    */
1011   properties [PROP_RUNNING_TIME] =
1012     g_param_spec_int64 ("running-time",
1013                         "Running Time",
1014                         "The amount of elapsed time performing the current build",
1015                         0,
1016                         G_MAXINT64,
1017                         0,
1018                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1019 
1020   /**
1021    * IdeBuildManager:warning-count:
1022    *
1023    * The "warning-count" property contains the number of warnings that have
1024    * been discovered in the current build request.
1025    *
1026    * Since: 3.32
1027    */
1028   properties [PROP_WARNING_COUNT] =
1029     g_param_spec_uint ("warning-count",
1030                        "Warning Count",
1031                        "The number of warnings that have been seen in the current build",
1032                        0, G_MAXUINT, 0,
1033                        (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1034 
1035   g_object_class_install_properties (object_class, N_PROPS, properties);
1036 
1037   /**
1038    * IdeBuildManager::build-started:
1039    * @self: An #IdeBuildManager
1040    * @pipeline: An #IdePipeline
1041    *
1042    * The "build-started" signal is emitted when a new build has started.
1043    * The build may be an incremental build. The @pipeline instance is
1044    * the build pipeline which is being executed.
1045    *
1046    * Since: 3.32
1047    */
1048   signals [BUILD_STARTED] =
1049     g_signal_new_class_handler ("build-started",
1050                                 G_TYPE_FROM_CLASS (klass),
1051                                 G_SIGNAL_RUN_LAST,
1052                                 G_CALLBACK (ide_build_manager_real_build_started),
1053                                 NULL, NULL,
1054                                 NULL,
1055                                 G_TYPE_NONE, 1, IDE_TYPE_PIPELINE);
1056 
1057   /**
1058    * IdeBuildManager::build-failed:
1059    * @self: An #IdeBuildManager
1060    * @pipeline: An #IdePipeline
1061    *
1062    * The "build-failed" signal is emitted when a build that was previously
1063    * notified via #IdeBuildManager::build-started has failed to complete
1064    * successfully.
1065    *
1066    * Contrast this with #IdeBuildManager::build-finished for a successful
1067    * build.
1068    *
1069    * Since: 3.32
1070    */
1071   signals [BUILD_FAILED] =
1072     g_signal_new_class_handler ("build-failed",
1073                                 G_TYPE_FROM_CLASS (klass),
1074                                 G_SIGNAL_RUN_LAST,
1075                                 G_CALLBACK (ide_build_manager_real_build_failed),
1076                                 NULL, NULL,
1077                                 NULL,
1078                                 G_TYPE_NONE, 1, IDE_TYPE_PIPELINE);
1079 
1080   /**
1081    * IdeBuildManager::build-finished:
1082    * @self: An #IdeBuildManager
1083    * @pipeline: An #IdePipeline
1084    *
1085    * The "build-finished" signal is emitted when a build completed
1086    * successfully.
1087    *
1088    * Since: 3.32
1089    */
1090   signals [BUILD_FINISHED] =
1091     g_signal_new_class_handler ("build-finished",
1092                                 G_TYPE_FROM_CLASS (klass),
1093                                 G_SIGNAL_RUN_LAST,
1094                                 G_CALLBACK (ide_build_manager_real_build_finished),
1095                                 NULL, NULL,
1096                                 NULL,
1097                                 G_TYPE_NONE, 1, IDE_TYPE_PIPELINE);
1098 }
1099 
1100 static void
ide_build_manager_action_cancel(IdeBuildManager * self,GVariant * param)1101 ide_build_manager_action_cancel (IdeBuildManager *self,
1102                                  GVariant        *param)
1103 {
1104   IDE_ENTRY;
1105 
1106   g_assert (IDE_IS_BUILD_MANAGER (self));
1107 
1108   ide_build_manager_cancel (self);
1109 
1110   IDE_EXIT;
1111 }
1112 
1113 static void
ide_build_manager_action_build(IdeBuildManager * self,GVariant * param)1114 ide_build_manager_action_build (IdeBuildManager *self,
1115                                 GVariant        *param)
1116 {
1117   IDE_ENTRY;
1118 
1119   g_assert (IDE_IS_BUILD_MANAGER (self));
1120 
1121   ide_build_manager_build_async (self, IDE_PIPELINE_PHASE_BUILD, NULL, NULL, NULL, NULL);
1122 
1123   IDE_EXIT;
1124 }
1125 
1126 static void
ide_build_manager_action_rebuild(IdeBuildManager * self,GVariant * param)1127 ide_build_manager_action_rebuild (IdeBuildManager *self,
1128                                   GVariant        *param)
1129 {
1130   IDE_ENTRY;
1131 
1132   g_assert (IDE_IS_BUILD_MANAGER (self));
1133 
1134   ide_build_manager_rebuild_async (self, IDE_PIPELINE_PHASE_BUILD, NULL, NULL, NULL, NULL);
1135 
1136   IDE_EXIT;
1137 }
1138 
1139 static void
ide_build_manager_action_clean(IdeBuildManager * self,GVariant * param)1140 ide_build_manager_action_clean (IdeBuildManager *self,
1141                                 GVariant        *param)
1142 {
1143   IDE_ENTRY;
1144 
1145   g_assert (IDE_IS_BUILD_MANAGER (self));
1146 
1147   ide_build_manager_clean_async (self, IDE_PIPELINE_PHASE_BUILD, NULL, NULL, NULL);
1148 
1149   IDE_EXIT;
1150 }
1151 
1152 static void
ide_build_manager_action_install(IdeBuildManager * self,GVariant * param)1153 ide_build_manager_action_install (IdeBuildManager *self,
1154                                   GVariant        *param)
1155 {
1156   IDE_ENTRY;
1157 
1158   g_assert (IDE_IS_BUILD_MANAGER (self));
1159 
1160   ide_build_manager_build_async (self, IDE_PIPELINE_PHASE_INSTALL, NULL, NULL, NULL, NULL);
1161 
1162   IDE_EXIT;
1163 }
1164 
1165 static void
ide_build_manager_action_export(IdeBuildManager * self,GVariant * param)1166 ide_build_manager_action_export (IdeBuildManager *self,
1167                                  GVariant        *param)
1168 {
1169   IDE_ENTRY;
1170 
1171   g_assert (IDE_IS_BUILD_MANAGER (self));
1172 
1173   ide_build_manager_build_async (self, IDE_PIPELINE_PHASE_EXPORT, NULL, NULL, NULL, NULL);
1174 
1175   IDE_EXIT;
1176 }
1177 
1178 static void
ide_build_manager_init(IdeBuildManager * self)1179 ide_build_manager_init (IdeBuildManager *self)
1180 {
1181   IDE_ENTRY;
1182 
1183   ide_build_manager_update_action_enabled (self);
1184 
1185   self->cancellable = g_cancellable_new ();
1186   self->needs_rediagnose = TRUE;
1187 
1188   self->pipeline_signals = dzl_signal_group_new (IDE_TYPE_PIPELINE);
1189 
1190   dzl_signal_group_connect_object (self->pipeline_signals,
1191                                    "diagnostic",
1192                                    G_CALLBACK (ide_build_manager_handle_diagnostic),
1193                                    self,
1194                                    G_CONNECT_SWAPPED);
1195 
1196   dzl_signal_group_connect_object (self->pipeline_signals,
1197                                    "notify::busy",
1198                                    G_CALLBACK (ide_build_manager_notify_busy),
1199                                    self,
1200                                    G_CONNECT_SWAPPED);
1201 
1202   dzl_signal_group_connect_object (self->pipeline_signals,
1203                                    "notify::message",
1204                                    G_CALLBACK (ide_build_manager_notify_message),
1205                                    self,
1206                                    G_CONNECT_SWAPPED);
1207 
1208   dzl_signal_group_connect_object (self->pipeline_signals,
1209                                    "started",
1210                                    G_CALLBACK (ide_build_manager_pipeline_started),
1211                                    self,
1212                                    G_CONNECT_SWAPPED);
1213 
1214   dzl_signal_group_connect_object (self->pipeline_signals,
1215                                    "finished",
1216                                    G_CALLBACK (ide_build_manager_pipeline_finished),
1217                                    self,
1218                                    G_CONNECT_SWAPPED);
1219 
1220 
1221   IDE_EXIT;
1222 }
1223 
1224 /**
1225  * ide_build_manager_get_busy:
1226  * @self: An #IdeBuildManager
1227  *
1228  * Gets if the #IdeBuildManager is currently busy building the project.
1229  *
1230  * See #IdeBuildManager:busy for more information.
1231  *
1232  * Since: 3.32
1233  */
1234 gboolean
ide_build_manager_get_busy(IdeBuildManager * self)1235 ide_build_manager_get_busy (IdeBuildManager *self)
1236 {
1237   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), FALSE);
1238 
1239   if G_LIKELY (self->pipeline != NULL)
1240     return ide_pipeline_get_busy (self->pipeline);
1241 
1242   return FALSE;
1243 }
1244 
1245 /**
1246  * ide_build_manager_get_message:
1247  * @self: An #IdeBuildManager
1248  *
1249  * This function returns the current build message as a string.
1250  *
1251  * See #IdeBuildManager:message for more information.
1252  *
1253  * Returns: (transfer full): A string containing the build message or %NULL
1254  *
1255  * Since: 3.32
1256  */
1257 gchar *
ide_build_manager_get_message(IdeBuildManager * self)1258 ide_build_manager_get_message (IdeBuildManager *self)
1259 {
1260   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), NULL);
1261 
1262   if G_LIKELY (self->pipeline != NULL)
1263     return ide_pipeline_get_message (self->pipeline);
1264 
1265   return NULL;
1266 }
1267 
1268 /**
1269  * ide_build_manager_get_last_build_time:
1270  * @self: An #IdeBuildManager
1271  *
1272  * This function returns a #GDateTime of the last build request. If
1273  * there has not yet been a build request, this will return %NULL.
1274  *
1275  * See #IdeBuildManager:last-build-time for more information.
1276  *
1277  * Returns: (nullable) (transfer none): a #GDateTime or %NULL.
1278  *
1279  * Since: 3.32
1280  */
1281 GDateTime *
ide_build_manager_get_last_build_time(IdeBuildManager * self)1282 ide_build_manager_get_last_build_time (IdeBuildManager *self)
1283 {
1284   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), NULL);
1285 
1286   return self->last_build_time;
1287 }
1288 
1289 /**
1290  * ide_build_manager_get_running_time:
1291  *
1292  * Gets the amount of elapsed time of the current build as a
1293  * #GTimeSpan.
1294  *
1295  * Returns: a #GTimeSpan containing the elapsed time of the build.
1296  *
1297  * Since: 3.32
1298  */
1299 GTimeSpan
ide_build_manager_get_running_time(IdeBuildManager * self)1300 ide_build_manager_get_running_time (IdeBuildManager *self)
1301 {
1302   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), 0);
1303 
1304   if (self->running_time != NULL)
1305     return g_timer_elapsed (self->running_time, NULL) * G_TIME_SPAN_SECOND;
1306 
1307   return 0;
1308 }
1309 
1310 /**
1311  * ide_build_manager_cancel:
1312  * @self: An #IdeBuildManager
1313  *
1314  * This function will cancel any in-flight builds.
1315  *
1316  * You may also activate this using the "cancel" #GAction provided
1317  * by the #GActionGroup interface.
1318  *
1319  * Since: 3.32
1320  */
1321 void
ide_build_manager_cancel(IdeBuildManager * self)1322 ide_build_manager_cancel (IdeBuildManager *self)
1323 {
1324   g_autoptr(GCancellable) cancellable = NULL;
1325 
1326   IDE_ENTRY;
1327 
1328   g_return_if_fail (IDE_IS_BUILD_MANAGER (self));
1329 
1330   cancellable = g_steal_pointer (&self->cancellable);
1331   self->cancellable = g_cancellable_new ();
1332 
1333   g_debug ("Cancelling [%p] build due to user request", cancellable);
1334 
1335   if (!g_cancellable_is_cancelled (cancellable))
1336     g_cancellable_cancel (cancellable);
1337 
1338   if (self->pipeline != NULL)
1339     _ide_pipeline_cancel (self->pipeline);
1340 
1341   IDE_EXIT;
1342 }
1343 
1344 /**
1345  * ide_build_manager_get_pipeline:
1346  * @self: An #IdeBuildManager
1347  *
1348  * This function gets the current build pipeline. The pipeline will be
1349  * reloaded as build configurations change.
1350  *
1351  * Returns: (transfer none) (nullable): An #IdePipeline.
1352  *
1353  * Since: 3.32
1354  */
1355 IdePipeline *
ide_build_manager_get_pipeline(IdeBuildManager * self)1356 ide_build_manager_get_pipeline (IdeBuildManager *self)
1357 {
1358   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
1359   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), NULL);
1360 
1361   return self->pipeline;
1362 }
1363 
1364 /**
1365  * ide_build_manager_ref_pipeline:
1366  * @self: a #IdeBuildManager
1367  *
1368  * A thread-safe variant of ide_build_manager_get_pipeline().
1369  *
1370  * Returns: (transfer full) (nullable): an #IdePipeline or %NULL
1371  *
1372  * Since: 3.32
1373  */
1374 IdePipeline *
ide_build_manager_ref_pipeline(IdeBuildManager * self)1375 ide_build_manager_ref_pipeline (IdeBuildManager *self)
1376 {
1377   IdePipeline *ret = NULL;
1378 
1379   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), NULL);
1380 
1381   ide_object_lock (IDE_OBJECT (self));
1382   g_set_object (&ret, self->pipeline);
1383   ide_object_unlock (IDE_OBJECT (self));
1384 
1385   return g_steal_pointer (&ret);
1386 }
1387 
1388 static void
ide_build_manager_build_targets_cb(GObject * object,GAsyncResult * result,gpointer user_data)1389 ide_build_manager_build_targets_cb (GObject      *object,
1390                                     GAsyncResult *result,
1391                                     gpointer      user_data)
1392 {
1393   IdePipeline *pipeline = (IdePipeline *)object;
1394   g_autoptr(IdeTask) task = user_data;
1395   g_autoptr(GError) error = NULL;
1396 
1397   IDE_ENTRY;
1398 
1399   g_assert (IDE_IS_PIPELINE (pipeline));
1400   g_assert (G_IS_ASYNC_RESULT (result));
1401   g_assert (IDE_IS_TASK (task));
1402 
1403   if (!ide_pipeline_build_targets_finish (pipeline, result, &error))
1404     {
1405       ide_object_warning (pipeline, "%s", error->message);
1406       ide_task_return_error (task, g_steal_pointer (&error));
1407       IDE_GOTO (failure);
1408     }
1409 
1410   ide_task_return_boolean (task, TRUE);
1411 
1412 failure:
1413   IDE_EXIT;
1414 }
1415 
1416 static void
ide_build_manager_save_all_cb(GObject * object,GAsyncResult * result,gpointer user_data)1417 ide_build_manager_save_all_cb (GObject      *object,
1418                                GAsyncResult *result,
1419                                gpointer      user_data)
1420 {
1421   IdeBufferManager *buffer_manager = (IdeBufferManager *)object;
1422   g_autoptr(IdeTask) task = user_data;
1423   g_autoptr(GError) error = NULL;
1424   IdeBuildManager *self;
1425   GCancellable *cancellable;
1426   GPtrArray *targets;
1427   IdePipelinePhase phase;
1428 
1429   IDE_ENTRY;
1430 
1431   g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
1432   g_assert (IDE_IS_TASK (task));
1433 
1434   self = ide_task_get_source_object (task);
1435   cancellable = ide_task_get_cancellable (task);
1436   targets = ide_task_get_task_data (task);
1437 
1438   g_assert (IDE_IS_BUILD_MANAGER (self));
1439   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
1440 
1441   if (!ide_buffer_manager_save_all_finish (buffer_manager, result, &error))
1442     {
1443       ide_task_return_error (task, g_steal_pointer (&error));
1444       IDE_EXIT;
1445     }
1446 
1447   phase = ide_pipeline_get_requested_phase (self->pipeline);
1448 
1449   ide_pipeline_build_targets_async (self->pipeline,
1450                                     phase,
1451                                     targets,
1452                                     cancellable,
1453                                     ide_build_manager_build_targets_cb,
1454                                     g_steal_pointer (&task));
1455 
1456   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_DIAGNOSTICS]);
1457   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LAST_BUILD_TIME]);
1458   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RUNNING_TIME]);
1459 
1460   IDE_EXIT;
1461 }
1462 
1463 /**
1464  * ide_build_manager_build_async:
1465  * @self: An #IdeBuildManager
1466  * @phase: An #IdePipelinePhase or 0
1467  * @targets: (nullable) (element-type IdeBuildTarget): an array of
1468  *   #IdeBuildTarget to build
1469  * @cancellable: (nullable): a #GCancellable or %NULL
1470  * @callback: A callback to execute upon completion
1471  * @user_data: user data for @callback
1472  *
1473  * This function will request that @phase is completed in the underlying
1474  * build pipeline and execute a build. Upon completion, @callback will be
1475  * executed and it can determine the success or failure of the operation
1476  * using ide_build_manager_build_finish().
1477  *
1478  * Since: 3.32
1479  */
1480 void
ide_build_manager_build_async(IdeBuildManager * self,IdePipelinePhase phase,GPtrArray * targets,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1481 ide_build_manager_build_async (IdeBuildManager     *self,
1482                                IdePipelinePhase     phase,
1483                                GPtrArray           *targets,
1484                                GCancellable        *cancellable,
1485                                GAsyncReadyCallback  callback,
1486                                gpointer             user_data)
1487 {
1488   g_autoptr(IdeTask) task = NULL;
1489   IdeBufferManager *buffer_manager;
1490   IdeContext *context;
1491 
1492   IDE_ENTRY;
1493 
1494   g_return_if_fail (IDE_IS_BUILD_MANAGER (self));
1495   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
1496   g_return_if_fail (!g_cancellable_is_cancelled (self->cancellable));
1497 
1498   cancellable = dzl_cancellable_chain (cancellable, self->cancellable);
1499 
1500   task = ide_task_new (self, cancellable, callback, user_data);
1501   ide_task_set_source_tag (task, ide_build_manager_build_async);
1502   ide_task_set_priority (task, G_PRIORITY_LOW);
1503   ide_task_set_return_on_cancel (task, TRUE);
1504 
1505   if (targets != NULL)
1506     ide_task_set_task_data (task, _g_ptr_array_copy_objects (targets), g_ptr_array_unref);
1507 
1508   if (self->pipeline == NULL ||
1509       self->can_build == FALSE ||
1510       !ide_pipeline_is_ready (self->pipeline))
1511     {
1512       ide_task_return_new_error (task,
1513                                  G_IO_ERROR,
1514                                  G_IO_ERROR_PENDING,
1515                                  "Cannot execute pipeline, it has not yet been prepared");
1516       IDE_EXIT;
1517     }
1518 
1519   if (!ide_pipeline_request_phase (self->pipeline, phase))
1520     {
1521       ide_task_return_boolean (task, TRUE);
1522       IDE_EXIT;
1523     }
1524 
1525   /*
1526    * Only update our "build time" if we are advancing to IDE_PIPELINE_PHASE_BUILD,
1527    * we don't really care about "builds" for configure stages and less.
1528    */
1529   if ((phase & IDE_PIPELINE_PHASE_MASK) >= IDE_PIPELINE_PHASE_BUILD)
1530     {
1531       g_clear_pointer (&self->last_build_time, g_date_time_unref);
1532       self->last_build_time = g_date_time_new_now_local ();
1533       self->diagnostic_count = 0;
1534       self->warning_count = 0;
1535       self->error_count = 0;
1536     }
1537 
1538   ide_build_manager_reset_info (self);
1539 
1540   /*
1541    * If we are performing a real build (not just something like configure),
1542    * then we want to ensure we save all the buffers. We don't want to do this
1543    * on every keypress (and execute_async() could be called on every keypress)
1544    * for ensuring build flags are up to date.
1545    */
1546   if ((phase & IDE_PIPELINE_PHASE_MASK) >= IDE_PIPELINE_PHASE_BUILD)
1547     {
1548       context = ide_object_get_context (IDE_OBJECT (self));
1549       buffer_manager = ide_buffer_manager_from_context (context);
1550       ide_buffer_manager_save_all_async (buffer_manager,
1551                                          NULL,
1552                                          ide_build_manager_save_all_cb,
1553                                          g_steal_pointer (&task));
1554       IDE_EXIT;
1555     }
1556 
1557   ide_pipeline_build_targets_async (self->pipeline,
1558                                     phase,
1559                                     targets,
1560                                     cancellable,
1561                                     ide_build_manager_build_targets_cb,
1562                                     g_steal_pointer (&task));
1563 
1564   IDE_EXIT;
1565 }
1566 
1567 /**
1568  * ide_build_manager_build_finish:
1569  * @self: An #IdeBuildManager
1570  * @result: a #GAsyncResult
1571  * @error: A location for a #GError or %NULL
1572  *
1573  * Completes a request to ide_build_manager_build_async().
1574  *
1575  * Returns: %TRUE if successful, otherwise %FALSE and @error is set.
1576  *
1577  * Since: 3.32
1578  */
1579 gboolean
ide_build_manager_build_finish(IdeBuildManager * self,GAsyncResult * result,GError ** error)1580 ide_build_manager_build_finish (IdeBuildManager  *self,
1581                                 GAsyncResult     *result,
1582                                 GError          **error)
1583 {
1584   gboolean ret;
1585 
1586   IDE_ENTRY;
1587 
1588   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), FALSE);
1589   g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
1590 
1591   ret = ide_task_propagate_boolean (IDE_TASK (result), error);
1592 
1593   IDE_RETURN (ret);
1594 }
1595 
1596 static void
ide_build_manager_clean_cb(GObject * object,GAsyncResult * result,gpointer user_data)1597 ide_build_manager_clean_cb (GObject      *object,
1598                             GAsyncResult *result,
1599                             gpointer      user_data)
1600 {
1601   IdePipeline *pipeline = (IdePipeline *)object;
1602   g_autoptr(IdeTask) task = user_data;
1603   g_autoptr(GError) error = NULL;
1604 
1605   IDE_ENTRY;
1606 
1607   g_assert (IDE_IS_PIPELINE (pipeline));
1608   g_assert (G_IS_ASYNC_RESULT (result));
1609   g_assert (IDE_IS_TASK (task));
1610 
1611   if (!ide_pipeline_clean_finish (pipeline, result, &error))
1612     ide_task_return_error (task, g_steal_pointer (&error));
1613   else
1614     ide_task_return_boolean (task, TRUE);
1615 
1616   IDE_EXIT;
1617 }
1618 
1619 /**
1620  * ide_build_manager_clean_async:
1621  * @self: a #IdeBuildManager
1622  * @phase: the build phase to clean
1623  * @cancellable: (nullable): a #GCancellable or %NULL
1624  * @callback: (nullable): a callback to execute upon completion, or %NULL
1625  * @user_data: closure data for @callback
1626  *
1627  * Asynchronously requests that the build pipeline clean up to @phase.
1628  *
1629  * See ide_pipeline_clean_async() for more information.
1630  *
1631  * Since: 3.32
1632  */
1633 void
ide_build_manager_clean_async(IdeBuildManager * self,IdePipelinePhase phase,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1634 ide_build_manager_clean_async (IdeBuildManager     *self,
1635                                IdePipelinePhase     phase,
1636                                GCancellable        *cancellable,
1637                                GAsyncReadyCallback  callback,
1638                                gpointer             user_data)
1639 {
1640   g_autoptr(IdeTask) task = NULL;
1641 
1642   IDE_ENTRY;
1643 
1644   g_return_if_fail (IDE_IS_BUILD_MANAGER (self));
1645   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
1646   g_return_if_fail (!g_cancellable_is_cancelled (self->cancellable));
1647 
1648   cancellable = dzl_cancellable_chain (cancellable, self->cancellable);
1649 
1650   task = ide_task_new (self, cancellable, callback, user_data);
1651   ide_task_set_source_tag (task, ide_build_manager_clean_async);
1652   ide_task_set_priority (task, G_PRIORITY_LOW);
1653   ide_task_set_return_on_cancel (task, TRUE);
1654 
1655   if (self->pipeline == NULL)
1656     {
1657       ide_task_return_new_error (task,
1658                                  G_IO_ERROR,
1659                                  G_IO_ERROR_PENDING,
1660                                  "Cannot execute pipeline, it has not yet been prepared");
1661       IDE_EXIT;
1662     }
1663 
1664   ide_build_manager_reset_info (self);
1665 
1666   ide_pipeline_clean_async (self->pipeline,
1667                             phase,
1668                             cancellable,
1669                             ide_build_manager_clean_cb,
1670                             g_steal_pointer (&task));
1671 
1672   IDE_EXIT;
1673 }
1674 
1675 /**
1676  * ide_build_manager_clean_finish:
1677  * @self: a #IdeBuildManager
1678  * @result: a #GAsyncResult
1679  * @error: a location for a #GError, or %NULL
1680  *
1681  * Completes an asynchronous request to ide_build_manager_clean_async().
1682  *
1683  * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
1684  *
1685  * Since: 3.32
1686  */
1687 gboolean
ide_build_manager_clean_finish(IdeBuildManager * self,GAsyncResult * result,GError ** error)1688 ide_build_manager_clean_finish (IdeBuildManager  *self,
1689                                 GAsyncResult     *result,
1690                                 GError          **error)
1691 {
1692   gboolean ret;
1693 
1694   IDE_ENTRY;
1695 
1696   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), FALSE);
1697   g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
1698 
1699   ret = ide_task_propagate_boolean (IDE_TASK (result), error);
1700 
1701   IDE_RETURN (ret);
1702 }
1703 
1704 static void
ide_build_manager_rebuild_cb(GObject * object,GAsyncResult * result,gpointer user_data)1705 ide_build_manager_rebuild_cb (GObject      *object,
1706                               GAsyncResult *result,
1707                               gpointer      user_data)
1708 {
1709   IdePipeline *pipeline = (IdePipeline *)object;
1710   g_autoptr(IdeTask) task = user_data;
1711   g_autoptr(GError) error = NULL;
1712 
1713   IDE_ENTRY;
1714 
1715   g_assert (IDE_IS_PIPELINE (pipeline));
1716   g_assert (G_IS_ASYNC_RESULT (result));
1717   g_assert (IDE_IS_TASK (task));
1718 
1719   if (!ide_pipeline_rebuild_finish (pipeline, result, &error))
1720     ide_task_return_error (task, g_steal_pointer (&error));
1721   else
1722     ide_task_return_boolean (task, TRUE);
1723 
1724   IDE_EXIT;
1725 }
1726 
1727 /**
1728  * ide_build_manager_rebuild_async:
1729  * @self: a #IdeBuildManager
1730  * @phase: the build phase to rebuild to
1731  * @targets: (element-type IdeBuildTarget) (nullable): an array of #GPtrArray
1732  *   of #IdeBuildTarget or %NULL.
1733  * @cancellable: (nullable): a #GCancellable or %NULL
1734  * @callback: (nullable): a callback to execute upon completion, or %NULL
1735  * @user_data: closure data for @callback
1736  *
1737  * Asynchronously requests that the build pipeline clean and rebuild up
1738  * to the given phase. This may involve discarding previous build artifacts
1739  * to allow for the rebuild process.
1740  *
1741  * See ide_pipeline_rebuild_async() for more information.
1742  *
1743  * Since: 3.32
1744  */
1745 void
ide_build_manager_rebuild_async(IdeBuildManager * self,IdePipelinePhase phase,GPtrArray * targets,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1746 ide_build_manager_rebuild_async (IdeBuildManager     *self,
1747                                  IdePipelinePhase        phase,
1748                                  GPtrArray           *targets,
1749                                  GCancellable        *cancellable,
1750                                  GAsyncReadyCallback  callback,
1751                                  gpointer             user_data)
1752 {
1753   g_autoptr(IdeTask) task = NULL;
1754 
1755   IDE_ENTRY;
1756 
1757   g_return_if_fail (IDE_IS_BUILD_MANAGER (self));
1758   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
1759   g_return_if_fail (!g_cancellable_is_cancelled (self->cancellable));
1760 
1761   cancellable = dzl_cancellable_chain (cancellable, self->cancellable);
1762 
1763   task = ide_task_new (self, cancellable, callback, user_data);
1764   ide_task_set_source_tag (task, ide_build_manager_rebuild_async);
1765   ide_task_set_priority (task, G_PRIORITY_LOW);
1766   ide_task_set_return_on_cancel (task, TRUE);
1767 
1768   if (self->pipeline == NULL)
1769     {
1770       ide_task_return_new_error (task,
1771                                  G_IO_ERROR,
1772                                  G_IO_ERROR_PENDING,
1773                                  "Cannot execute pipeline, it has not yet been prepared");
1774       IDE_EXIT;
1775     }
1776 
1777   ide_build_manager_reset_info (self);
1778 
1779   ide_pipeline_rebuild_async (self->pipeline,
1780                               phase,
1781                               targets,
1782                               cancellable,
1783                               ide_build_manager_rebuild_cb,
1784                               g_steal_pointer (&task));
1785 
1786   IDE_EXIT;
1787 }
1788 
1789 /**
1790  * ide_build_manager_rebuild_finish:
1791  * @self: a #IdeBuildManager
1792  * @result: a #GAsyncResult
1793  * @error: a location for a #GError, or %NULL
1794  *
1795  * Completes an asynchronous request to ide_build_manager_rebuild_async().
1796  *
1797  * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
1798  *
1799  * Since: 3.32
1800  */
1801 gboolean
ide_build_manager_rebuild_finish(IdeBuildManager * self,GAsyncResult * result,GError ** error)1802 ide_build_manager_rebuild_finish (IdeBuildManager  *self,
1803                                   GAsyncResult     *result,
1804                                   GError          **error)
1805 {
1806   gboolean ret;
1807 
1808   IDE_ENTRY;
1809 
1810   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), FALSE);
1811   g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
1812 
1813   ret = ide_task_propagate_boolean (IDE_TASK (result), error);
1814 
1815   IDE_RETURN (ret);
1816 }
1817 
1818 /**
1819  * ide_build_manager_get_can_build:
1820  * @self: a #IdeBuildManager
1821  *
1822  * Checks if the current pipeline is ready to build.
1823  *
1824  * Returns: %TRUE if a build operation can advance the pipeline.
1825  *
1826  * Since: 3.32
1827  */
1828 gboolean
ide_build_manager_get_can_build(IdeBuildManager * self)1829 ide_build_manager_get_can_build (IdeBuildManager *self)
1830 {
1831   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), FALSE);
1832 
1833   return self->can_build;
1834 }
1835 
1836 static void
ide_build_manager_set_can_build(IdeBuildManager * self,gboolean can_build)1837 ide_build_manager_set_can_build (IdeBuildManager *self,
1838                                  gboolean         can_build)
1839 {
1840   g_return_if_fail (IDE_IS_BUILD_MANAGER (self));
1841 
1842   can_build = !!can_build;
1843 
1844   if (self->can_build != can_build)
1845     {
1846       self->can_build = can_build;
1847       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_BUILD]);
1848       ide_build_manager_update_action_enabled (self);
1849     }
1850 }
1851 
1852 /**
1853  * ide_build_manager_invalidate:
1854  * @self: a #IdeBuildManager
1855  *
1856  * Requests that the #IdeBuildManager invalidate the current pipeline and
1857  * setup a new pipeline.
1858  *
1859  * Since: 3.32
1860  */
1861 void
ide_build_manager_invalidate(IdeBuildManager * self)1862 ide_build_manager_invalidate (IdeBuildManager *self)
1863 {
1864   g_return_if_fail (IDE_IS_MAIN_THREAD ());
1865   g_return_if_fail (IDE_IS_BUILD_MANAGER (self));
1866 
1867   ide_build_manager_invalidate_pipeline (self);
1868 }
1869 
1870 guint
ide_build_manager_get_error_count(IdeBuildManager * self)1871 ide_build_manager_get_error_count (IdeBuildManager *self)
1872 {
1873   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), 0);
1874 
1875   return self->error_count;
1876 }
1877 
1878 guint
ide_build_manager_get_warning_count(IdeBuildManager * self)1879 ide_build_manager_get_warning_count (IdeBuildManager *self)
1880 {
1881   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), 0);
1882 
1883   return self->warning_count;
1884 }
1885 
1886 void
_ide_build_manager_start(IdeBuildManager * self)1887 _ide_build_manager_start (IdeBuildManager *self)
1888 {
1889   g_return_if_fail (IDE_IS_BUILD_MANAGER (self));
1890   g_return_if_fail (self->started == FALSE);
1891 
1892   self->started = TRUE;
1893 
1894   ide_build_manager_invalidate (self);
1895 }
1896