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