1 /* ide-build-system.c
2 *
3 * Copyright 2015-2019 Christian Hergert <christian@hergert.me>
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-system"
22
23 #include "config.h"
24
25 #include <libide-code.h>
26 #include <libide-io.h>
27 #include <libide-projects.h>
28 #include <libide-threading.h>
29 #include <libide-vcs.h>
30 #include <string.h>
31
32 #include "ide-gfile-private.h"
33
34 #include "ide-build-manager.h"
35 #include "ide-pipeline.h"
36 #include "ide-build-system.h"
37 #include "ide-config.h"
38 #include "ide-device.h"
39 #include "ide-foundry-compat.h"
40 #include "ide-toolchain.h"
41
42 G_DEFINE_INTERFACE (IdeBuildSystem, ide_build_system, IDE_TYPE_OBJECT)
43
44 enum {
45 PROP_0,
46 PROP_PROJECT_FILE,
47 N_PROPS
48 };
49
50 typedef struct
51 {
52 GPtrArray *files;
53 GHashTable *flags;
54 guint index;
55 } GetBuildFlagsData;
56
57 static GParamSpec *properties [N_PROPS];
58
59 static void
get_build_flags_data_free(GetBuildFlagsData * data)60 get_build_flags_data_free (GetBuildFlagsData *data)
61 {
62 g_clear_pointer (&data->files, g_ptr_array_unref);
63 g_clear_pointer (&data->flags, g_hash_table_unref);
64 g_slice_free (GetBuildFlagsData, data);
65 }
66
67 gint
ide_build_system_get_priority(IdeBuildSystem * self)68 ide_build_system_get_priority (IdeBuildSystem *self)
69 {
70 IdeBuildSystemInterface *iface;
71
72 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), 0);
73
74 iface = IDE_BUILD_SYSTEM_GET_IFACE (self);
75
76 if (iface->get_priority != NULL)
77 return iface->get_priority (self);
78
79 return 0;
80 }
81
82 static void
ide_build_system_real_get_build_flags_async(IdeBuildSystem * self,GFile * file,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)83 ide_build_system_real_get_build_flags_async (IdeBuildSystem *self,
84 GFile *file,
85 GCancellable *cancellable,
86 GAsyncReadyCallback callback,
87 gpointer user_data)
88 {
89 g_autoptr(IdeContext) context = NULL;
90 g_autoptr(IdeTask) task = NULL;
91 g_autoptr(GError) error = NULL;
92 g_auto(GStrv) parsed_flags = NULL;
93 IdeBuildManager *build_manager;
94 IdeEnvironment *env;
95 const gchar *flags = NULL;
96 const gchar *path;
97 IdePipeline *pipeline;
98 IdeConfig *config;
99
100 g_assert (IDE_IS_MAIN_THREAD ());
101 g_assert (IDE_IS_BUILD_SYSTEM (self));
102 g_assert (G_IS_FILE (file));
103 g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
104
105 task = ide_task_new (self, cancellable, callback, user_data);
106 ide_task_set_source_tag (task, ide_build_system_real_get_build_flags_async);
107
108 /* Avoid work immediately if we can */
109 if (ide_task_return_error_if_cancelled (task))
110 return;
111
112 if (!g_file_is_native (file) || !(path = g_file_peek_path (file)))
113 {
114 ide_task_return_new_error (task,
115 G_IO_ERROR,
116 G_IO_ERROR_NOT_SUPPORTED,
117 "Cannot get build flags for non-native file");
118 return;
119 }
120
121 if (!(context = ide_object_ref_context (IDE_OBJECT (self))) ||
122 !ide_context_has_project (context) ||
123 !(build_manager = ide_build_manager_from_context (context)) ||
124 !(pipeline = ide_build_manager_get_pipeline (build_manager)) ||
125 !(config = ide_pipeline_get_config (pipeline)) ||
126 !(env = ide_config_get_environment (config)))
127 {
128 ide_task_return_new_error (task,
129 G_IO_ERROR,
130 G_IO_ERROR_NOT_INITIALIZED,
131 "Cannot access build flags without build config");
132 return;
133 }
134
135 if (ide_path_is_cpp_like (path))
136 {
137 flags = ide_environment_getenv (env, "CXXFLAGS");
138 }
139 else if (ide_path_is_c_like (path))
140 {
141 if (!(flags = ide_environment_getenv (env, "CFLAGS")))
142 flags = ide_environment_getenv (env, "CXXFLAGS");
143 }
144 else
145 {
146 ide_task_return_new_error (task,
147 G_IO_ERROR,
148 G_IO_ERROR_NOT_SUPPORTED,
149 "Cannot extract build flags for unknown file type: \"%s\"",
150 path);
151 return;
152 }
153
154 if (flags == NULL)
155 {
156 ide_task_return_new_error (task,
157 G_IO_ERROR,
158 G_IO_ERROR_NOT_SUPPORTED,
159 "No CFLAGS or CXXFLAGS environment variables were specified");
160 return;
161 }
162
163 if (!g_shell_parse_argv (flags, NULL, &parsed_flags, &error))
164 ide_task_return_error (task, g_steal_pointer (&error));
165 else
166 ide_task_return_pointer (task, g_steal_pointer (&parsed_flags), g_strfreev);
167 }
168
169 static gchar **
ide_build_system_real_get_build_flags_finish(IdeBuildSystem * self,GAsyncResult * result,GError ** error)170 ide_build_system_real_get_build_flags_finish (IdeBuildSystem *self,
171 GAsyncResult *result,
172 GError **error)
173 {
174 return ide_task_propagate_pointer (IDE_TASK (result), error);
175 }
176
177 static void
ide_build_system_get_build_flags_cb(GObject * object,GAsyncResult * result,gpointer user_data)178 ide_build_system_get_build_flags_cb (GObject *object,
179 GAsyncResult *result,
180 gpointer user_data)
181 {
182 IdeBuildSystem *self = (IdeBuildSystem *)object;
183 g_autoptr(IdeTask) task = user_data;
184 g_autoptr(GError) error = NULL;
185 g_auto(GStrv) flags = NULL;
186 GetBuildFlagsData *data;
187 GFile *file;
188
189 g_assert (IDE_IS_BUILD_SYSTEM (self));
190 g_assert (G_IS_ASYNC_RESULT (result));
191 g_assert (IDE_IS_TASK (task));
192
193 data = ide_task_get_task_data (task);
194 g_assert (data != NULL);
195 g_assert (data->files != NULL);
196 g_assert (data->files->len > 0);
197 g_assert (data->index < data->files->len);
198 g_assert (data->flags != NULL);
199
200 file = g_ptr_array_index (data->files, data->index);
201 g_assert (G_IS_FILE (file));
202
203 data->index++;
204
205 flags = ide_build_system_get_build_flags_finish (self, result, &error);
206
207 if (error != NULL)
208 g_debug ("Failed to load build flags for \"%s\": %s",
209 g_file_peek_path (file),
210 error->message);
211 else
212 g_hash_table_insert (data->flags, g_object_ref (file), g_steal_pointer (&flags));
213
214 if (ide_task_return_error_if_cancelled (task))
215 return;
216
217 if (data->index < data->files->len)
218 {
219 GCancellable *cancellable = ide_task_get_cancellable (task);
220
221 file = g_ptr_array_index (data->files, data->index);
222 g_assert (G_IS_FILE (file));
223
224 ide_build_system_get_build_flags_async (self,
225 file,
226 cancellable,
227 ide_build_system_get_build_flags_cb,
228 g_steal_pointer (&task));
229 return;
230 }
231
232 ide_task_return_pointer (task,
233 g_steal_pointer (&data->flags),
234 g_hash_table_unref);
235 }
236
237 static GPtrArray *
copy_files(GPtrArray * in)238 copy_files (GPtrArray *in)
239 {
240 GPtrArray *out = g_ptr_array_new_full (in->len, g_object_unref);
241 for (guint i = 0; i < in->len; i++)
242 g_ptr_array_add (out, g_file_dup (g_ptr_array_index (in, i)));
243 return g_steal_pointer (&out);
244 }
245
246 static void
ide_build_system_real_get_build_flags_for_files_async(IdeBuildSystem * self,GPtrArray * files,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)247 ide_build_system_real_get_build_flags_for_files_async (IdeBuildSystem *self,
248 GPtrArray *files,
249 GCancellable *cancellable,
250 GAsyncReadyCallback callback,
251 gpointer user_data)
252 {
253 g_autoptr(IdeTask) task = NULL;
254 GetBuildFlagsData *data;
255
256 g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
257 g_return_if_fail (files != NULL);
258 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
259
260 task = ide_task_new (self, cancellable, callback, user_data);
261 ide_task_set_source_tag (task, ide_build_system_real_get_build_flags_for_files_async);
262 ide_task_set_priority (task, G_PRIORITY_LOW);
263
264 if (files->len == 0)
265 {
266 ide_task_return_new_error (task,
267 G_IO_ERROR,
268 G_IO_ERROR_INVAL,
269 "No files were provided");
270 return;
271 }
272
273 g_assert (files->len > 0);
274 g_assert (G_IS_FILE (g_ptr_array_index (files, 0)));
275
276 if (ide_task_return_error_if_cancelled (task))
277 return;
278
279 data = g_slice_new0 (GetBuildFlagsData);
280 data->files = copy_files (files);
281 data->flags = g_hash_table_new_full ((GHashFunc)g_file_hash,
282 (GEqualFunc)g_file_equal,
283 g_object_unref,
284 (GDestroyNotify)g_strfreev);
285 ide_task_set_task_data (task, data, get_build_flags_data_free);
286
287 ide_build_system_get_build_flags_async (self,
288 g_ptr_array_index (files, 0),
289 cancellable,
290 ide_build_system_get_build_flags_cb,
291 g_steal_pointer (&task));
292 }
293
294 static GHashTable *
ide_build_system_real_get_build_flags_for_files_finish(IdeBuildSystem * self,GAsyncResult * result,GError ** error)295 ide_build_system_real_get_build_flags_for_files_finish (IdeBuildSystem *self,
296 GAsyncResult *result,
297 GError **error)
298 {
299 return ide_task_propagate_pointer (IDE_TASK (result), error);
300 }
301
302 static void
ide_build_system_default_init(IdeBuildSystemInterface * iface)303 ide_build_system_default_init (IdeBuildSystemInterface *iface)
304 {
305 iface->get_build_flags_async = ide_build_system_real_get_build_flags_async;
306 iface->get_build_flags_finish = ide_build_system_real_get_build_flags_finish;
307 iface->get_build_flags_for_files_async = ide_build_system_real_get_build_flags_for_files_async;
308 iface->get_build_flags_for_files_finish = ide_build_system_real_get_build_flags_for_files_finish;
309
310 properties [PROP_PROJECT_FILE] =
311 g_param_spec_object ("project-file",
312 "Project File",
313 "The project file.",
314 G_TYPE_FILE,
315 (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
316
317 g_object_interface_install_property (iface, properties [PROP_PROJECT_FILE]);
318 }
319
320 static gchar *
ide_build_system_translate(IdeBuildSystem * self,IdePipeline * pipeline,const gchar * prefix,const gchar * path)321 ide_build_system_translate (IdeBuildSystem *self,
322 IdePipeline *pipeline,
323 const gchar *prefix,
324 const gchar *path)
325 {
326 g_autofree gchar *freeme = NULL;
327 g_autofree gchar *translated_path = NULL;
328 g_autoptr(GFile) file = NULL;
329 g_autoptr(GFile) translated = NULL;
330 IdeRuntime *runtime;
331
332 g_assert (IDE_IS_BUILD_SYSTEM (self));
333 g_assert (!pipeline || IDE_IS_PIPELINE (pipeline));
334 g_assert (prefix != NULL);
335 g_assert (path != NULL);
336
337 if (NULL == pipeline ||
338 NULL == (runtime = ide_pipeline_get_runtime (pipeline)))
339 return g_strdup_printf ("%s%s", prefix, path);
340
341 if (!g_path_is_absolute (path))
342 path = freeme = ide_pipeline_build_builddir_path (pipeline, path, NULL);
343
344 file = g_file_new_for_path (path);
345 translated = ide_runtime_translate_file (runtime, file);
346 translated_path = g_file_get_path (translated);
347
348 return g_strdup_printf ("%s%s", prefix, translated_path);
349 }
350
351 static void
ide_build_system_post_process_build_flags(IdeBuildSystem * self,gchar ** flags)352 ide_build_system_post_process_build_flags (IdeBuildSystem *self,
353 gchar **flags)
354 {
355 IdePipeline *pipeline;
356 IdeBuildManager *build_manager;
357 IdeContext *context;
358
359 g_assert (IDE_IS_BUILD_SYSTEM (self));
360
361 if (flags == NULL || flags[0] == NULL)
362 return;
363
364 context = ide_object_get_context (IDE_OBJECT (self));
365 build_manager = ide_build_manager_from_context (context);
366 pipeline = ide_build_manager_get_pipeline (build_manager);
367
368 for (guint i = 0; flags[i] != NULL; i++)
369 {
370 gchar *flag = flags[i];
371 gchar *translated;
372
373 if (flag[0] != '-')
374 continue;
375
376 switch (flag[1])
377 {
378 case 'I':
379 if (flag[2] == '\0')
380 {
381 if (flags[i+1] != NULL)
382 {
383 translated = ide_build_system_translate (self, pipeline, "", flags[++i]);
384 flags[i] = translated;
385 g_free (flag);
386 }
387 }
388 else
389 {
390 translated = ide_build_system_translate (self, pipeline, "-I", &flag[2]);
391 flags[i] = translated;
392 g_free (flag);
393 }
394 break;
395
396 case 'D':
397 case 'x':
398 if (strlen (flag) == 2)
399 i++;
400 break;
401
402 case 'f': /* -fPIC */
403 case 'W': /* -Werror... */
404 case 'm': /* -m64 -mtune=native */
405 default:
406 break;
407 }
408 }
409 }
410
411 void
ide_build_system_get_build_flags_async(IdeBuildSystem * self,GFile * file,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)412 ide_build_system_get_build_flags_async (IdeBuildSystem *self,
413 GFile *file,
414 GCancellable *cancellable,
415 GAsyncReadyCallback callback,
416 gpointer user_data)
417 {
418 IDE_ENTRY;
419
420 g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
421 g_return_if_fail (G_IS_FILE (file));
422 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
423
424 IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_async (self, file, cancellable, callback, user_data);
425
426 IDE_EXIT;
427 }
428
429 /**
430 * ide_build_system_get_build_flags_finish:
431 *
432 * Returns: (transfer full):
433 *
434 * Since: 3.32
435 */
436 gchar **
ide_build_system_get_build_flags_finish(IdeBuildSystem * self,GAsyncResult * result,GError ** error)437 ide_build_system_get_build_flags_finish (IdeBuildSystem *self,
438 GAsyncResult *result,
439 GError **error)
440 {
441 gchar **ret;
442
443 IDE_ENTRY;
444
445 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
446 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
447
448 ret = IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_finish (self, result, error);
449 if (ret != NULL)
450 ide_build_system_post_process_build_flags (self, ret);
451
452 #ifdef IDE_ENABLE_TRACE
453 if (ret != NULL)
454 {
455 g_autoptr(GString) str = g_string_new (NULL);
456 for (guint i = 0; ret[i]; i++)
457 {
458 g_autofree char *escaped = g_shell_quote (ret[i]);
459 g_string_append (str, escaped);
460 g_string_append_c (str, ' ');
461 }
462 IDE_TRACE_MSG ("build_flags = %s", str->str);
463 }
464 #endif
465
466 IDE_RETURN (ret);
467 }
468
469 /**
470 * ide_build_system_get_build_flags_for_files_async:
471 * @self: An #IdeBuildSystem instance.
472 * @files: (element-type GFile) (transfer none): array of files whose build flags has to be retrieved.
473 * @cancellable: (allow-none): a #GCancellable to cancel getting build flags.
474 * @callback: function to be called after getting build flags.
475 * @user_data: data to pass to @callback.
476 *
477 * This function will get build flags for all files and returns
478 * map of file and its build flags as #GHashTable.
479 *
480 * Since: 3.32
481 */
482 void
ide_build_system_get_build_flags_for_files_async(IdeBuildSystem * self,GPtrArray * files,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)483 ide_build_system_get_build_flags_for_files_async (IdeBuildSystem *self,
484 GPtrArray *files,
485 GCancellable *cancellable,
486 GAsyncReadyCallback callback,
487 gpointer user_data)
488 {
489 IDE_ENTRY;
490
491 g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
492 g_return_if_fail ( files != NULL);
493 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
494
495 IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_for_files_async (self, files, cancellable, callback, user_data);
496
497 IDE_EXIT;
498 }
499
500 /**
501 * ide_build_system_get_build_flags_for_files_finish:
502 * @self: an #IdeBuildSystem
503 * @result: a #GAsyncResult
504 * @error: a location for a #GError or %NULL
505 *
506 * Returns: (element-type Ide.File GStrv) (transfer full): a #GHashTable or #GFile to #GStrv
507 *
508 * Since: 3.32
509 */
510 GHashTable *
ide_build_system_get_build_flags_for_files_finish(IdeBuildSystem * self,GAsyncResult * result,GError ** error)511 ide_build_system_get_build_flags_for_files_finish (IdeBuildSystem *self,
512 GAsyncResult *result,
513 GError **error)
514 {
515 GHashTable *ret;
516
517 IDE_ENTRY;
518
519 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
520 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
521
522 ret = IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_for_files_finish (self, result, error);
523
524 if (ret != NULL)
525 {
526 GHashTableIter iter;
527 gchar **flags;
528
529 g_hash_table_iter_init (&iter, ret);
530
531 while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&flags))
532 ide_build_system_post_process_build_flags (self, flags);
533 }
534
535 IDE_RETURN (ret);
536 }
537
538 gchar *
ide_build_system_get_builddir(IdeBuildSystem * self,IdePipeline * pipeline)539 ide_build_system_get_builddir (IdeBuildSystem *self,
540 IdePipeline *pipeline)
541 {
542 gchar *ret = NULL;
543
544 IDE_ENTRY;
545
546 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
547 g_return_val_if_fail (IDE_IS_PIPELINE (pipeline), NULL);
548
549 if (IDE_BUILD_SYSTEM_GET_IFACE (self)->get_builddir)
550 ret = IDE_BUILD_SYSTEM_GET_IFACE (self)->get_builddir (self, pipeline);
551
552 if (ret == NULL)
553 {
554 g_autofree gchar *name = NULL;
555 g_autofree gchar *branch = NULL;
556 g_autoptr(GFile) base = NULL;
557 g_autoptr(GFile) nosymlink = NULL;
558 IdeConfig *config;
559 const gchar *config_id;
560 const gchar *runtime_id;
561 const gchar *arch;
562 IdeRuntime *runtime;
563 IdeContext *context;
564 IdeVcs *vcs;
565
566 context = ide_object_get_context (IDE_OBJECT (self));
567 vcs = ide_vcs_from_context (context);
568 config = ide_pipeline_get_config (pipeline);
569 config_id = ide_config_get_id (config);
570 runtime = ide_pipeline_get_runtime (pipeline);
571 runtime_id = ide_runtime_get_short_id (runtime);
572 branch = ide_vcs_get_branch_name (vcs);
573 arch = ide_pipeline_get_arch (pipeline);
574
575 if (branch != NULL)
576 name = g_strdup_printf ("%s-%s-%s-%s", config_id, runtime_id, arch, branch);
577 else
578 name = g_strdup_printf ("%s-%s-%s", config_id, runtime_id, arch);
579
580 g_strdelimit (name, "@:/ ", '-');
581
582 /* Avoid symlink's when we can, so that paths with symlinks have at least
583 * a chance of working.
584 */
585 base = ide_context_cache_file (context, "builds", name, NULL);
586 nosymlink = _ide_g_file_readlink (base);
587
588 ret = g_file_get_path (nosymlink);
589 }
590
591 IDE_RETURN (ret);
592 }
593
594 gchar *
ide_build_system_get_id(IdeBuildSystem * self)595 ide_build_system_get_id (IdeBuildSystem *self)
596 {
597 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
598
599 if (IDE_BUILD_SYSTEM_GET_IFACE (self)->get_id)
600 return IDE_BUILD_SYSTEM_GET_IFACE (self)->get_id (self);
601
602 return g_strdup (G_OBJECT_TYPE_NAME (self));
603 }
604
605 gchar *
ide_build_system_get_display_name(IdeBuildSystem * self)606 ide_build_system_get_display_name (IdeBuildSystem *self)
607 {
608 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
609
610 if (IDE_BUILD_SYSTEM_GET_IFACE (self)->get_display_name)
611 return IDE_BUILD_SYSTEM_GET_IFACE (self)->get_display_name (self);
612
613 return ide_build_system_get_id (self);
614 }
615
616 static void
ide_build_system_get_build_flags_for_dir_cb2(GObject * object,GAsyncResult * result,gpointer user_data)617 ide_build_system_get_build_flags_for_dir_cb2 (GObject *object,
618 GAsyncResult *result,
619 gpointer user_data)
620 {
621 IdeBuildSystem *build_system = (IdeBuildSystem *)object;
622 g_autoptr(IdeTask) task = user_data;
623 g_autoptr(GError) error = NULL;
624 g_autoptr(GHashTable) ret = NULL;
625
626 g_assert (IDE_IS_BUILD_SYSTEM (build_system));
627 g_assert (G_IS_ASYNC_RESULT (result));
628 g_assert (IDE_IS_TASK (task));
629
630 ret = ide_build_system_get_build_flags_for_files_finish (build_system, result, &error);
631
632 if (ret == NULL)
633 ide_task_return_error (task, g_steal_pointer (&error));
634 else
635 ide_task_return_pointer (task,
636 g_steal_pointer (&ret),
637 g_hash_table_unref);
638 }
639
640 static void
ide_build_system_get_build_flags_for_dir_cb(GObject * object,GAsyncResult * result,gpointer user_data)641 ide_build_system_get_build_flags_for_dir_cb (GObject *object,
642 GAsyncResult *result,
643 gpointer user_data)
644 {
645 GFile *dir = (GFile *)object;
646 g_autoptr(IdeTask) task = user_data;
647 g_autoptr(GError) error = NULL;
648 g_autoptr(GPtrArray) infos = NULL;
649 g_autoptr(GPtrArray) files = NULL;
650 IdeBuildSystem *self;
651 GCancellable *cancellable;
652 IdeContext *context;
653 IdeVcs *vcs;
654
655 g_assert (G_IS_FILE (dir));
656 g_assert (G_IS_ASYNC_RESULT (result));
657 g_assert (IDE_IS_TASK (task));
658
659 infos = ide_g_file_get_children_finish (dir, result, &error);
660 IDE_PTR_ARRAY_SET_FREE_FUNC (infos, g_object_unref);
661
662 if (infos == NULL)
663 {
664 ide_task_return_error (task, g_steal_pointer (&error));
665 return;
666 }
667
668 self = ide_task_get_source_object (task);
669 context = ide_object_get_context (IDE_OBJECT (self));
670 vcs = ide_vcs_from_context (context);
671 cancellable = ide_task_get_cancellable (task);
672 files = g_ptr_array_new_with_free_func (g_object_unref);
673
674 for (guint i = 0; i < infos->len; i++)
675 {
676 GFileInfo *file_info = g_ptr_array_index (infos, i);
677 GFileType file_type = g_file_info_get_file_type (file_info);
678
679 if (file_type == G_FILE_TYPE_REGULAR)
680 {
681 const gchar *name = g_file_info_get_name (file_info);
682 g_autoptr(GFile) child = g_file_get_child (dir, name);
683
684 if (!ide_vcs_is_ignored (vcs, child, NULL))
685 g_ptr_array_add (files, g_steal_pointer (&child));
686 }
687 }
688
689 ide_build_system_get_build_flags_for_files_async (self,
690 files,
691 cancellable,
692 ide_build_system_get_build_flags_for_dir_cb2,
693 g_steal_pointer (&task));
694 }
695
696 void
ide_build_system_get_build_flags_for_dir_async(IdeBuildSystem * self,GFile * directory,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)697 ide_build_system_get_build_flags_for_dir_async (IdeBuildSystem *self,
698 GFile *directory,
699 GCancellable *cancellable,
700 GAsyncReadyCallback callback,
701 gpointer user_data)
702 {
703 g_autoptr(IdeTask) task = NULL;
704
705 g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
706 g_return_if_fail (G_IS_FILE (directory));
707 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
708
709 task = ide_task_new (self, cancellable, callback, user_data);
710 ide_task_set_source_tag (task, ide_build_system_get_build_flags_for_dir_async);
711 ide_task_set_priority (task, G_PRIORITY_LOW);
712
713 ide_g_file_get_children_async (directory,
714 G_FILE_ATTRIBUTE_STANDARD_NAME","
715 G_FILE_ATTRIBUTE_STANDARD_TYPE,
716 G_FILE_QUERY_INFO_NONE,
717 G_PRIORITY_LOW,
718 cancellable,
719 ide_build_system_get_build_flags_for_dir_cb,
720 g_steal_pointer (&task));
721 }
722
723 /**
724 * ide_build_system_get_build_flags_for_dir_finish:
725 * @self: an #IdeBuildSystem
726 * @result: a #GAsyncResult
727 * @error: a location for a #GError or %NULL
728 *
729 * Returns: (element-type Ide.File GStrv) (transfer full): a #GHashTable of #GFile to #GStrv
730 *
731 * Since: 3.32
732 */
733 GHashTable *
ide_build_system_get_build_flags_for_dir_finish(IdeBuildSystem * self,GAsyncResult * result,GError ** error)734 ide_build_system_get_build_flags_for_dir_finish (IdeBuildSystem *self,
735 GAsyncResult *result,
736 GError **error)
737 {
738 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
739 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
740
741 return ide_task_propagate_pointer (IDE_TASK (result), error);
742 }
743
744 /**
745 * ide_build_system_supports_toolchain:
746 * @self: an #IdeBuildSystem
747 * @toolchain: a #IdeToolchain
748 *
749 * Checks whether the build system supports the given toolchain.
750 *
751 * Returns: %TRUE if the toolchain is supported by the build system, %FALSE otherwise
752 *
753 * Since: 3.32
754 */
755 gboolean
ide_build_system_supports_toolchain(IdeBuildSystem * self,IdeToolchain * toolchain)756 ide_build_system_supports_toolchain (IdeBuildSystem *self,
757 IdeToolchain *toolchain)
758 {
759 const gchar *toolchain_id;
760
761 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), FALSE);
762 g_return_val_if_fail (IDE_IS_TOOLCHAIN (toolchain), FALSE);
763
764 toolchain_id = ide_toolchain_get_id (toolchain);
765 if (g_strcmp0 (toolchain_id, "default") == 0)
766 return TRUE;
767
768 if (IDE_BUILD_SYSTEM_GET_IFACE (self)->supports_toolchain)
769 return IDE_BUILD_SYSTEM_GET_IFACE (self)->supports_toolchain (self, toolchain);
770
771 return FALSE;
772 }
773
774 /**
775 * ide_build_system_get_project_version:
776 * @self: a #IdeBuildSystem
777 *
778 * If the build system supports it, gets the project version as configured
779 * in the build system's configuration files.
780 *
781 * Returns: (transfer full) (nullable): a string containing the project version
782 *
783 * Since: 3.32
784 */
785 gchar *
ide_build_system_get_project_version(IdeBuildSystem * self)786 ide_build_system_get_project_version (IdeBuildSystem *self)
787 {
788 g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
789 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
790
791 if (IDE_BUILD_SYSTEM_GET_IFACE (self)->get_project_version)
792 return IDE_BUILD_SYSTEM_GET_IFACE (self)->get_project_version (self);
793
794 return NULL;
795 }
796
797 /**
798 * ide_build_system_supports_language:
799 * @self: a #IdeBuildSystem
800 * @language: the language identifier
801 *
802 * Returns %TRUE if @self in it's current configuration is known to support @language.
803 *
804 * Returns: %TRUE if @language is supported, otherwise %FALSE.
805 *
806 * Since: 41.0
807 */
808 gboolean
ide_build_system_supports_language(IdeBuildSystem * self,const char * language)809 ide_build_system_supports_language (IdeBuildSystem *self,
810 const char *language)
811 {
812 g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), FALSE);
813 g_return_val_if_fail (language != NULL, FALSE);
814
815 if (IDE_BUILD_SYSTEM_GET_IFACE (self)->supports_language)
816 return IDE_BUILD_SYSTEM_GET_IFACE (self)->supports_language (self, language);
817
818 return FALSE;
819 }
820