1 /* ide-runtime.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-runtime"
22 
23 #include "config.h"
24 
25 #include <dazzle.h>
26 #include <glib/gi18n.h>
27 #include <libide-threading.h>
28 #include <string.h>
29 
30 #include "ide-build-target.h"
31 #include "ide-config.h"
32 #include "ide-config-manager.h"
33 #include "ide-runtime.h"
34 #include "ide-runner.h"
35 #include "ide-toolchain.h"
36 #include "ide-triplet.h"
37 
38 typedef struct
39 {
40   gchar *id;
41   gchar *short_id;
42   gchar *category;
43   gchar *name;
44   gchar *display_name;
45 } IdeRuntimePrivate;
46 
47 G_DEFINE_TYPE_WITH_PRIVATE (IdeRuntime, ide_runtime, IDE_TYPE_OBJECT)
48 
49 enum {
50   PROP_0,
51   PROP_ID,
52   PROP_SHORT_ID,
53   PROP_CATEGORY,
54   PROP_DISPLAY_NAME,
55   PROP_NAME,
56   N_PROPS
57 };
58 
59 static GParamSpec *properties [N_PROPS];
60 
61 static IdeSubprocessLauncher *
ide_runtime_real_create_launcher(IdeRuntime * self,GError ** error)62 ide_runtime_real_create_launcher (IdeRuntime  *self,
63                                   GError     **error)
64 {
65   IdeSubprocessLauncher *ret;
66 
67   IDE_ENTRY;
68 
69   g_assert (IDE_IS_RUNTIME (self));
70 
71   ret = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE);
72 
73   if (ret != NULL)
74     {
75       ide_subprocess_launcher_set_run_on_host (ret, TRUE);
76       ide_subprocess_launcher_set_clear_env (ret, FALSE);
77     }
78   else
79     {
80       g_set_error (error,
81                    G_IO_ERROR,
82                    G_IO_ERROR_FAILED,
83                    "An unknown error ocurred");
84     }
85 
86   IDE_RETURN (ret);
87 }
88 
89 static gboolean
ide_runtime_real_contains_program_in_path(IdeRuntime * self,const gchar * program,GCancellable * cancellable)90 ide_runtime_real_contains_program_in_path (IdeRuntime   *self,
91                                            const gchar  *program,
92                                            GCancellable *cancellable)
93 {
94   g_assert (IDE_IS_RUNTIME (self));
95   g_assert (program != NULL);
96   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
97 
98   if (!ide_is_flatpak ())
99     {
100       g_autofree gchar *path = NULL;
101       path = g_find_program_in_path (program);
102       return path != NULL;
103     }
104   else
105     {
106       g_autoptr(IdeSubprocessLauncher) launcher = NULL;
107 
108       /*
109        * If we are in flatpak, we have to execute a program on the host to
110        * determine if there is a program available, as we cannot resolve
111        * file paths from inside the mount namespace.
112        */
113 
114       if (NULL != (launcher = ide_runtime_create_launcher (self, NULL)))
115         {
116           g_autoptr(IdeSubprocess) subprocess = NULL;
117 
118           ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
119           ide_subprocess_launcher_push_argv (launcher, "which");
120           ide_subprocess_launcher_push_argv (launcher, program);
121 
122           if (NULL != (subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, NULL)))
123             return ide_subprocess_wait_check (subprocess, NULL, NULL);
124         }
125 
126       return FALSE;
127     }
128 
129   g_assert_not_reached ();
130 }
131 
132 gboolean
ide_runtime_contains_program_in_path(IdeRuntime * self,const gchar * program,GCancellable * cancellable)133 ide_runtime_contains_program_in_path (IdeRuntime   *self,
134                                       const gchar  *program,
135                                       GCancellable *cancellable)
136 {
137   g_return_val_if_fail (IDE_IS_RUNTIME (self), FALSE);
138   g_return_val_if_fail (program != NULL, FALSE);
139   g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
140 
141   return IDE_RUNTIME_GET_CLASS (self)->contains_program_in_path (self, program, cancellable);
142 }
143 
144 static void
ide_runtime_real_prepare_configuration(IdeRuntime * self,IdeConfig * config)145 ide_runtime_real_prepare_configuration (IdeRuntime *self,
146                                         IdeConfig  *config)
147 {
148   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
149 
150   g_assert (IDE_IS_RUNTIME (self));
151   g_assert (IDE_IS_CONFIG (config));
152 
153   if (!ide_config_get_prefix_set (config))
154     {
155       g_autofree gchar *install_path = NULL;
156       g_autofree gchar *project_id = NULL;
157       IdeContext *context;
158 
159       context = ide_object_get_context (IDE_OBJECT (self));
160       project_id = ide_context_dup_project_id (context);
161 
162       install_path = g_build_filename (g_get_user_cache_dir (),
163                                        "gnome-builder",
164                                        "install",
165                                        project_id,
166                                        priv->id,
167                                        NULL);
168 
169       ide_config_set_prefix (config, install_path);
170       ide_config_set_prefix_set (config, FALSE);
171     }
172 }
173 
174 static IdeRunner *
ide_runtime_real_create_runner(IdeRuntime * self,IdeBuildTarget * build_target)175 ide_runtime_real_create_runner (IdeRuntime     *self,
176                                 IdeBuildTarget *build_target)
177 {
178   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
179   IdeEnvironment *env;
180   g_autoptr(GFile) installdir = NULL;
181   g_auto(GStrv) argv = NULL;
182   g_autofree gchar *cwd = NULL;
183   IdeConfigManager *config_manager;
184   const gchar *prefix;
185   IdeContext *context;
186   IdeRunner *runner;
187   IdeConfig *config;
188 
189   g_assert (IDE_IS_RUNTIME (self));
190   g_assert (!build_target || IDE_IS_BUILD_TARGET (build_target));
191 
192   context = ide_object_get_context (IDE_OBJECT (self));
193   g_assert (IDE_IS_CONTEXT (context));
194 
195   config_manager = ide_config_manager_from_context (context);
196   config = ide_config_manager_get_current (config_manager);
197   prefix = ide_config_get_prefix (config);
198 
199   runner = ide_runner_new (context);
200   g_assert (IDE_IS_RUNNER (runner));
201 
202   ide_object_append (IDE_OBJECT (self), IDE_OBJECT (runner));
203 
204   env = ide_runner_get_environment (runner);
205 
206   if (ide_str_equal0 (priv->id, "host"))
207     ide_runner_set_run_on_host (runner, TRUE);
208 
209   if (build_target != NULL)
210     {
211       ide_runner_set_build_target (runner, build_target);
212 
213       installdir = ide_build_target_get_install_directory (build_target);
214       argv = ide_build_target_get_argv (build_target);
215       cwd = ide_build_target_get_cwd (build_target);
216     }
217 
218   /* Possibly translate relative paths for the binary */
219   if (argv && argv[0] && !g_path_is_absolute (argv[0]))
220     {
221       const gchar *slash = strchr (argv[0], '/');
222       g_autofree gchar *copy = g_strdup (slash ? (slash + 1) : argv[0]);
223 
224       g_free (argv[0]);
225 
226       if (installdir != NULL)
227         {
228           g_autoptr(GFile) dest = g_file_get_child (installdir, copy);
229           argv[0] = g_file_get_path (dest);
230         }
231       else
232         argv[0] = g_steal_pointer (&copy);
233     }
234 
235   if (installdir != NULL)
236     {
237       g_autoptr(GFile) parentdir = NULL;
238       g_autofree gchar *schemadir = NULL;
239       g_autofree gchar *parentpath = NULL;
240 
241       /* GSettings requires an env var for non-standard dirs */
242       if ((parentdir = g_file_get_parent (installdir)))
243         {
244           parentpath = g_file_get_path (parentdir);
245           schemadir = g_build_filename (parentpath, "share", "glib-2.0", "schemas", NULL);
246           ide_environment_setenv (env, "GSETTINGS_SCHEMA_DIR", schemadir);
247         }
248     }
249 
250   if (prefix != NULL)
251     {
252       static const gchar *tries[] = { "lib64", "lib", "lib32", };
253       const gchar *old_path = ide_environment_getenv (env, "LD_LIBRARY_PATH");
254 
255       for (guint i = 0; i < G_N_ELEMENTS (tries); i++)
256         {
257           g_autofree gchar *ld_library_path = g_build_filename (prefix, tries[i], NULL);
258 
259           if (g_file_test (ld_library_path, G_FILE_TEST_IS_DIR))
260             {
261               if (old_path != NULL)
262                 {
263                   g_autofree gchar *freeme = g_steal_pointer (&ld_library_path);
264                   ld_library_path = g_strdup_printf ("%s:%s", freeme, old_path);
265                 }
266 
267               ide_environment_setenv (env, "LD_LIBRARY_PATH", ld_library_path);
268               break;
269             }
270         }
271     }
272 
273   if (argv != NULL)
274     ide_runner_push_args (runner, (const gchar * const *)argv);
275 
276   if (cwd != NULL)
277     ide_runner_set_cwd (runner, cwd);
278 
279   return runner;
280 }
281 
282 static GFile *
ide_runtime_real_translate_file(IdeRuntime * self,GFile * file)283 ide_runtime_real_translate_file (IdeRuntime *self,
284                                  GFile      *file)
285 {
286   g_autofree gchar *path = NULL;
287 
288   g_assert (IDE_IS_RUNTIME (self));
289   g_assert (G_IS_FILE (file));
290 
291   /* We only need to translate when running as flatpak */
292   if (!ide_is_flatpak ())
293     return NULL;
294 
295   /* Only deal with native files */
296   if (!g_file_is_native (file) || NULL == (path = g_file_get_path (file)))
297     return NULL;
298 
299   /* If this is /usr or /etc, then translate to /run/host/$dir,
300    * as that is where flatpak 0.10.1 and greater will mount them
301    * when --filesystem=host.
302    */
303   if (g_str_has_prefix (path, "/usr/") || g_str_has_prefix (path, "/etc/"))
304     return g_file_new_build_filename ("/run/host/", path, NULL);
305 
306   return NULL;
307 }
308 
309 static gchar *
ide_runtime_repr(IdeObject * object)310 ide_runtime_repr (IdeObject *object)
311 {
312   IdeRuntime *self = (IdeRuntime *)object;
313   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
314 
315   g_assert (IDE_IS_MAIN_THREAD ());
316   g_assert (IDE_IS_RUNTIME (self));
317 
318   return g_strdup_printf ("%s id=\"%s\" display-name=\"%s\"",
319                           G_OBJECT_TYPE_NAME (self),
320                           priv->id ?: "",
321                           priv->display_name ?: "");
322 }
323 
324 static void
ide_runtime_finalize(GObject * object)325 ide_runtime_finalize (GObject *object)
326 {
327   IdeRuntime *self = (IdeRuntime *)object;
328   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
329 
330   g_clear_pointer (&priv->id, g_free);
331   g_clear_pointer (&priv->short_id, g_free);
332   g_clear_pointer (&priv->display_name, g_free);
333   g_clear_pointer (&priv->name, g_free);
334 
335   G_OBJECT_CLASS (ide_runtime_parent_class)->finalize (object);
336 }
337 
338 static void
ide_runtime_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)339 ide_runtime_get_property (GObject    *object,
340                           guint       prop_id,
341                           GValue     *value,
342                           GParamSpec *pspec)
343 {
344   IdeRuntime *self = IDE_RUNTIME (object);
345 
346   switch (prop_id)
347     {
348     case PROP_ID:
349       g_value_set_string (value, ide_runtime_get_id (self));
350       break;
351 
352     case PROP_SHORT_ID:
353       g_value_set_string (value, ide_runtime_get_short_id (self));
354       break;
355 
356     case PROP_CATEGORY:
357       g_value_set_string (value, ide_runtime_get_category (self));
358       break;
359 
360     case PROP_DISPLAY_NAME:
361       g_value_set_string (value, ide_runtime_get_display_name (self));
362       break;
363 
364     case PROP_NAME:
365       g_value_set_string (value, ide_runtime_get_name (self));
366       break;
367 
368     default:
369       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370     }
371 }
372 
373 static void
ide_runtime_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)374 ide_runtime_set_property (GObject      *object,
375                           guint         prop_id,
376                           const GValue *value,
377                           GParamSpec   *pspec)
378 {
379   IdeRuntime *self = IDE_RUNTIME (object);
380 
381   switch (prop_id)
382     {
383     case PROP_ID:
384       ide_runtime_set_id (self, g_value_get_string (value));
385       break;
386 
387     case PROP_SHORT_ID:
388       ide_runtime_set_short_id (self, g_value_get_string (value));
389       break;
390 
391     case PROP_CATEGORY:
392       ide_runtime_set_category (self, g_value_get_string (value));
393       break;
394 
395     case PROP_DISPLAY_NAME:
396       ide_runtime_set_display_name (self, g_value_get_string (value));
397       break;
398 
399     case PROP_NAME:
400       ide_runtime_set_name (self, g_value_get_string (value));
401       break;
402 
403     default:
404       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
405     }
406 }
407 
408 static void
ide_runtime_class_init(IdeRuntimeClass * klass)409 ide_runtime_class_init (IdeRuntimeClass *klass)
410 {
411   GObjectClass *object_class = G_OBJECT_CLASS (klass);
412   IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
413 
414   object_class->finalize = ide_runtime_finalize;
415   object_class->get_property = ide_runtime_get_property;
416   object_class->set_property = ide_runtime_set_property;
417 
418   i_object_class->repr = ide_runtime_repr;
419 
420   klass->create_launcher = ide_runtime_real_create_launcher;
421   klass->create_runner = ide_runtime_real_create_runner;
422   klass->contains_program_in_path = ide_runtime_real_contains_program_in_path;
423   klass->prepare_configuration = ide_runtime_real_prepare_configuration;
424   klass->translate_file = ide_runtime_real_translate_file;
425 
426   properties [PROP_ID] =
427     g_param_spec_string ("id",
428                          "Id",
429                          "The runtime identifier",
430                          NULL,
431                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
432 
433   properties [PROP_SHORT_ID] =
434     g_param_spec_string ("short-id",
435                          "Short Id",
436                          "The short runtime identifier",
437                          NULL,
438                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
439 
440   properties [PROP_CATEGORY] =
441     g_param_spec_string ("category",
442                          "Category",
443                          "The runtime's category",
444                          NULL,
445                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
446 
447   properties [PROP_DISPLAY_NAME] =
448     g_param_spec_string ("display-name",
449                          "Display Name",
450                          "Display Name",
451                          NULL,
452                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
453 
454   properties [PROP_NAME] =
455     g_param_spec_string ("name",
456                          "Name",
457                          "Name",
458                          NULL,
459                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
460 
461   g_object_class_install_properties (object_class, N_PROPS, properties);
462 }
463 
464 static void
ide_runtime_init(IdeRuntime * self)465 ide_runtime_init (IdeRuntime *self)
466 {
467 }
468 
469 const gchar *
ide_runtime_get_id(IdeRuntime * self)470 ide_runtime_get_id (IdeRuntime  *self)
471 {
472   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
473 
474   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
475 
476   return priv->id;
477 }
478 
479 void
ide_runtime_set_id(IdeRuntime * self,const gchar * id)480 ide_runtime_set_id (IdeRuntime  *self,
481                     const gchar *id)
482 {
483   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
484 
485   g_return_if_fail (IDE_IS_RUNTIME (self));
486   g_return_if_fail (id != NULL);
487 
488   if (!ide_str_equal0 (id, priv->id))
489     {
490       g_free (priv->id);
491       priv->id = g_strdup (id);
492       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]);
493     }
494 }
495 
496 const gchar *
ide_runtime_get_short_id(IdeRuntime * self)497 ide_runtime_get_short_id (IdeRuntime  *self)
498 {
499   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
500 
501   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
502 
503   return priv->short_id ? priv->short_id : priv->id;
504 }
505 
506 void
ide_runtime_set_short_id(IdeRuntime * self,const gchar * short_id)507 ide_runtime_set_short_id (IdeRuntime  *self,
508                           const gchar *short_id)
509 {
510   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
511 
512   g_return_if_fail (IDE_IS_RUNTIME (self));
513   g_return_if_fail (short_id != NULL);
514 
515   if (!ide_str_equal0 (short_id, priv->short_id))
516     {
517       g_free (priv->short_id);
518       priv->short_id = g_strdup (short_id);
519       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHORT_ID]);
520     }
521 }
522 
523 const gchar *
ide_runtime_get_category(IdeRuntime * self)524 ide_runtime_get_category (IdeRuntime  *self)
525 {
526   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
527 
528   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
529   g_return_val_if_fail (priv->category != NULL, "Host System");
530 
531   return priv->category;
532 }
533 
534 void
ide_runtime_set_category(IdeRuntime * self,const gchar * category)535 ide_runtime_set_category (IdeRuntime  *self,
536                           const gchar *category)
537 {
538   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
539 
540   g_return_if_fail (IDE_IS_RUNTIME (self));
541 
542   if (category == NULL)
543     category = _("Host System");
544 
545   if (!ide_str_equal0 (category, priv->category))
546     {
547       g_free (priv->category);
548       priv->category = g_strdup (category);
549       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CATEGORY]);
550     }
551 }
552 
553 const gchar *
ide_runtime_get_name(IdeRuntime * self)554 ide_runtime_get_name (IdeRuntime *self)
555 {
556   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
557 
558   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
559 
560   return priv->name ? priv->name : priv->display_name;
561 }
562 
563 void
ide_runtime_set_name(IdeRuntime * self,const gchar * name)564 ide_runtime_set_name (IdeRuntime  *self,
565                       const gchar *name)
566 {
567   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
568 
569   g_return_if_fail (IDE_IS_RUNTIME (self));
570 
571   if (g_strcmp0 (name, priv->name) != 0)
572     {
573       g_free (priv->name);
574       priv->name = g_strdup (name);
575       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
576     }
577 }
578 
579 const gchar *
ide_runtime_get_display_name(IdeRuntime * self)580 ide_runtime_get_display_name (IdeRuntime *self)
581 {
582   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
583   gchar *ret;
584 
585   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
586 
587   if (!(ret = priv->display_name))
588     {
589       if (!(ret = priv->name))
590         ret = priv->id;
591     }
592 
593   return ret;
594 }
595 
596 void
ide_runtime_set_display_name(IdeRuntime * self,const gchar * display_name)597 ide_runtime_set_display_name (IdeRuntime  *self,
598                               const gchar *display_name)
599 {
600   IdeRuntimePrivate *priv = ide_runtime_get_instance_private (self);
601 
602   g_return_if_fail (IDE_IS_RUNTIME (self));
603 
604   if (g_strcmp0 (display_name, priv->display_name) != 0)
605     {
606       g_free (priv->display_name);
607       priv->display_name = g_strdup (display_name);
608       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISPLAY_NAME]);
609     }
610 }
611 
612 IdeRuntime *
ide_runtime_new(const gchar * id,const gchar * display_name)613 ide_runtime_new (const gchar *id,
614                  const gchar *display_name)
615 {
616   g_return_val_if_fail (id != NULL, NULL);
617   g_return_val_if_fail (display_name != NULL, NULL);
618 
619   return g_object_new (IDE_TYPE_RUNTIME,
620                        "id", id,
621                        "display-name", display_name,
622                        NULL);
623 }
624 
625 /**
626  * ide_runtime_create_launcher:
627  *
628  * Creates a launcher for the runtime.
629  *
630  * This can be used to execute a command within a runtime.
631  *
632  * It is important that this function can be run from a thread without
633  * side effects.
634  *
635  * Returns: (transfer full): An #IdeSubprocessLauncher or %NULL upon failure.
636  *
637  * Since: 3.32
638  */
639 IdeSubprocessLauncher *
ide_runtime_create_launcher(IdeRuntime * self,GError ** error)640 ide_runtime_create_launcher (IdeRuntime  *self,
641                              GError     **error)
642 {
643   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
644 
645   return IDE_RUNTIME_GET_CLASS (self)->create_launcher (self, error);
646 }
647 
648 void
ide_runtime_prepare_configuration(IdeRuntime * self,IdeConfig * configuration)649 ide_runtime_prepare_configuration (IdeRuntime       *self,
650                                    IdeConfig *configuration)
651 {
652   g_return_if_fail (IDE_IS_RUNTIME (self));
653   g_return_if_fail (IDE_IS_CONFIG (configuration));
654 
655   IDE_RUNTIME_GET_CLASS (self)->prepare_configuration (self, configuration);
656 }
657 
658 /**
659  * ide_runtime_create_runner:
660  * @self: An #IdeRuntime
661  * @build_target: (nullable): An #IdeBuildTarget or %NULL
662  *
663  * Creates a new runner that can be used to execute the build target within
664  * the runtime. This should be used to implement such features as "run target"
665  * or "run unit test" inside the target runtime.
666  *
667  * If @build_target is %NULL, the runtime should create a runner that allows
668  * the caller to specify the binary using the #IdeRunner API.
669  *
670  * Returns: (transfer full) (nullable): An #IdeRunner if successful, otherwise
671  *   %NULL and @error is set.
672  *
673  * Since: 3.32
674  */
675 IdeRunner *
ide_runtime_create_runner(IdeRuntime * self,IdeBuildTarget * build_target)676 ide_runtime_create_runner (IdeRuntime     *self,
677                            IdeBuildTarget *build_target)
678 {
679   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
680   g_return_val_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target), NULL);
681 
682   return IDE_RUNTIME_GET_CLASS (self)->create_runner (self, build_target);
683 }
684 
685 GQuark
ide_runtime_error_quark(void)686 ide_runtime_error_quark (void)
687 {
688   static GQuark quark = 0;
689 
690   if G_UNLIKELY (quark == 0)
691     quark = g_quark_from_static_string ("ide_runtime_error_quark");
692 
693   return quark;
694 }
695 
696 /**
697  * ide_runtime_translate_file:
698  * @self: An #IdeRuntime
699  * @file: a #GFile
700  *
701  * Translates the file from a path within the runtime to a path that can
702  * be accessed from the host system.
703  *
704  * Returns: (transfer full) (not nullable): a #GFile.
705  *
706  * Since: 3.32
707  */
708 GFile *
ide_runtime_translate_file(IdeRuntime * self,GFile * file)709 ide_runtime_translate_file (IdeRuntime *self,
710                             GFile      *file)
711 {
712   GFile *ret = NULL;
713 
714   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
715   g_return_val_if_fail (G_IS_FILE (file), NULL);
716 
717   if (IDE_RUNTIME_GET_CLASS (self)->translate_file)
718     ret = IDE_RUNTIME_GET_CLASS (self)->translate_file (self, file);
719 
720   if (ret == NULL)
721     ret = g_object_ref (file);
722 
723   return ret;
724 }
725 
726 /**
727  * ide_runtime_get_system_include_dirs:
728  * @self: a #IdeRuntime
729  *
730  * Gets the system include dirs for the runtime. Usually, this is just
731  * "/usr/include", but more complex runtimes may include additional.
732  *
733  * Returns: (transfer full) (array zero-terminated=1): A newly allocated
734  *   string containing the include dirs.
735  *
736  * Since: 3.32
737  */
738 gchar **
ide_runtime_get_system_include_dirs(IdeRuntime * self)739 ide_runtime_get_system_include_dirs (IdeRuntime *self)
740 {
741   static const gchar *basic[] = { "/usr/include", NULL };
742 
743   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
744 
745   if (IDE_RUNTIME_GET_CLASS (self)->get_system_include_dirs)
746     return IDE_RUNTIME_GET_CLASS (self)->get_system_include_dirs (self);
747 
748   return g_strdupv ((gchar **)basic);
749 }
750 
751 /**
752  * ide_runtime_get_triplet:
753  * @self: a #IdeRuntime
754  *
755  * Gets the architecture triplet of the runtime.
756  *
757  * This can be used to ensure we're compiling for the right architecture
758  * given the current device.
759  *
760  * Returns: (transfer full) (not nullable): the architecture triplet the runtime
761  * will build for.
762  *
763  * Since: 3.32
764  */
765 IdeTriplet *
ide_runtime_get_triplet(IdeRuntime * self)766 ide_runtime_get_triplet (IdeRuntime *self)
767 {
768   IdeTriplet *ret = NULL;
769 
770   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
771 
772   if (IDE_RUNTIME_GET_CLASS (self)->get_triplet)
773     ret = IDE_RUNTIME_GET_CLASS (self)->get_triplet (self);
774 
775   if (ret == NULL)
776     ret = ide_triplet_new_from_system ();
777 
778   return ret;
779 }
780 
781 /**
782  * ide_runtime_get_arch:
783  * @self: a #IdeRuntime
784  *
785  * Gets the architecture of the runtime.
786  *
787  * This can be used to ensure we're compiling for the right architecture
788  * given the current device.
789  *
790  * This is strictly equivalent to calling #ide_triplet_get_arch on the result
791  * of #ide_runtime_get_triplet.
792  *
793  * Returns: (transfer full) (not nullable): the name of the architecture
794  * the runtime will build for.
795  *
796  * Since: 3.32
797  */
798 gchar *
ide_runtime_get_arch(IdeRuntime * self)799 ide_runtime_get_arch (IdeRuntime *self)
800 {
801   gchar *ret = NULL;
802   g_autoptr(IdeTriplet) triplet = NULL;
803 
804   g_return_val_if_fail (IDE_IS_RUNTIME (self), NULL);
805 
806   triplet = ide_runtime_get_triplet (self);
807   ret = g_strdup (ide_triplet_get_arch (triplet));
808 
809   return ret;
810 }
811 
812 /**
813  * ide_runtime_supports_toolchain:
814  * @self: a #IdeRuntime
815  * @toolchain: the #IdeToolchain to check
816  *
817  * Informs wether a toolchain is supported by this.
818  *
819  * Returns: %TRUE if the toolchain is supported
820  *
821  * Since: 3.32
822  */
823 gboolean
ide_runtime_supports_toolchain(IdeRuntime * self,IdeToolchain * toolchain)824 ide_runtime_supports_toolchain (IdeRuntime   *self,
825                                 IdeToolchain *toolchain)
826 {
827   const gchar *toolchain_id;
828 
829   g_return_val_if_fail (IDE_IS_RUNTIME (self), FALSE);
830   g_return_val_if_fail (IDE_IS_TOOLCHAIN (toolchain), FALSE);
831 
832   toolchain_id = ide_toolchain_get_id (toolchain);
833   if (g_strcmp0 (toolchain_id, "default") == 0)
834     return TRUE;
835 
836   if (IDE_RUNTIME_GET_CLASS (self)->supports_toolchain)
837     return IDE_RUNTIME_GET_CLASS (self)->supports_toolchain (self, toolchain);
838 
839   return TRUE;
840 }
841