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