1 /* gb-flatpak-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 "gbp-flatpak-runtime"
22 
23 #include <glib/gi18n.h>
24 #include <json-glib/json-glib.h>
25 #include <libide-vcs.h>
26 
27 #include "gbp-flatpak-manifest.h"
28 #include "gbp-flatpak-runner.h"
29 #include "gbp-flatpak-runtime.h"
30 #include "gbp-flatpak-subprocess-launcher.h"
31 #include "gbp-flatpak-util.h"
32 
33 struct _GbpFlatpakRuntime
34 {
35   IdeRuntime parent_instance;
36 
37   GHashTable *program_paths_cache;
38 
39   IdeTriplet *triplet;
40   gchar *branch;
41   gchar *deploy_dir;
42   gchar *platform;
43   gchar *sdk;
44   gchar *runtime_dir;
45   GFile *deploy_dir_files;
46 };
47 
48 G_DEFINE_FINAL_TYPE (GbpFlatpakRuntime, gbp_flatpak_runtime, IDE_TYPE_RUNTIME)
49 
50 enum {
51   PROP_0,
52   PROP_TRIPLET,
53   PROP_BRANCH,
54   PROP_DEPLOY_DIR,
55   PROP_PLATFORM,
56   PROP_SDK,
57   N_PROPS
58 };
59 
60 static GParamSpec *properties [N_PROPS];
61 
62 static inline gboolean
strv_empty(gchar ** strv)63 strv_empty (gchar **strv)
64 {
65   return strv == NULL || strv[0] == NULL;
66 }
67 
68 static const gchar *
get_builddir(GbpFlatpakRuntime * self)69 get_builddir (GbpFlatpakRuntime *self)
70 {
71   g_autoptr(IdeContext) context = ide_object_ref_context (IDE_OBJECT (self));
72   g_autoptr(IdeBuildManager) build_manager = ide_build_manager_ref_from_context (context);
73   g_autoptr(IdePipeline) pipeline = ide_build_manager_ref_pipeline (build_manager);
74 
75   return ide_pipeline_get_builddir (pipeline);
76 }
77 
78 static gchar *
get_staging_directory(GbpFlatpakRuntime * self)79 get_staging_directory (GbpFlatpakRuntime *self)
80 {
81   g_autoptr(IdeContext) context = ide_object_ref_context (IDE_OBJECT (self));
82   g_autoptr(IdeBuildManager) build_manager = ide_build_manager_ref_from_context (context);
83   g_autoptr(IdePipeline) pipeline = ide_build_manager_ref_pipeline (build_manager);
84 
85   return gbp_flatpak_get_staging_dir (pipeline);
86 }
87 
88 static gboolean
gbp_flatpak_runtime_contains_program_in_path(IdeRuntime * runtime,const gchar * program,GCancellable * cancellable)89 gbp_flatpak_runtime_contains_program_in_path (IdeRuntime   *runtime,
90                                               const gchar  *program,
91                                               GCancellable *cancellable)
92 {
93   static const gchar *known_path_dirs[] = { "/bin" };
94   GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)runtime;
95   gboolean ret = FALSE;
96   gpointer val = NULL;
97 
98   g_assert (GBP_IS_FLATPAK_RUNTIME (self));
99   g_assert (program != NULL);
100   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
101 
102   if (g_hash_table_lookup_extended (self->program_paths_cache, program, NULL, &val))
103     return GPOINTER_TO_UINT (val);
104 
105   for (guint i = 0; i < G_N_ELEMENTS (known_path_dirs); i++)
106     {
107       g_autofree gchar *path = NULL;
108 
109       path = g_build_filename (self->deploy_dir,
110                                "files",
111                                known_path_dirs[i],
112                                program,
113                                NULL);
114 
115       if (g_file_test (path, G_FILE_TEST_IS_EXECUTABLE))
116         {
117           ret = TRUE;
118           break;
119         }
120     }
121 
122   /* If we have an SDK, we might need to check that runtime */
123   if (ret == FALSE &&
124       self->sdk != NULL &&
125       !ide_str_equal0 (self->platform, self->sdk))
126     {
127       IdeContext* context = ide_object_get_context (IDE_OBJECT (self));
128       if (context)
129         {
130           g_autoptr(IdeRuntimeManager) manager = ide_object_ensure_child_typed (IDE_OBJECT (context), IDE_TYPE_RUNTIME_MANAGER);
131           g_autofree char *arch = ide_runtime_get_arch (runtime);
132           g_autofree char *sdk_id = g_strdup_printf ("flatpak:%s/%s/%s", self->sdk, arch, self->branch);
133           IdeRuntime *sdk = ide_runtime_manager_get_runtime (manager, sdk_id);
134 
135           if (sdk != NULL && sdk != runtime)
136             ret = ide_runtime_contains_program_in_path (sdk, program, cancellable);
137         }
138     }
139 
140   /* Cache both positive and negative lookups */
141   g_hash_table_insert (self->program_paths_cache,
142                        (gchar *)g_intern_string (program),
143                        GUINT_TO_POINTER (ret));
144 
145   return ret;
146 }
147 
148 static IdeSubprocessLauncher *
gbp_flatpak_runtime_create_launcher(IdeRuntime * runtime,GError ** error)149 gbp_flatpak_runtime_create_launcher (IdeRuntime  *runtime,
150                                      GError     **error)
151 {
152   IdeSubprocessLauncher *ret;
153   GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)runtime;
154   g_autoptr(IdeContext) context = NULL;
155   const gchar *runtime_id;
156 
157   g_return_val_if_fail (GBP_IS_FLATPAK_RUNTIME (self), NULL);
158 
159   context = ide_object_ref_context (IDE_OBJECT (self));
160   g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
161 
162   runtime_id = ide_runtime_get_id (runtime);
163   g_return_val_if_fail (g_str_has_prefix (runtime_id, "flatpak:"), NULL);
164 
165   ret = gbp_flatpak_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE);
166 
167   if (ret != NULL && !ide_context_has_project (context))
168     {
169       ide_subprocess_launcher_set_cwd (ret, g_get_home_dir ());
170       ide_subprocess_launcher_set_run_on_host (ret, TRUE);
171       gbp_flatpak_subprocess_launcher_use_run (GBP_FLATPAK_SUBPROCESS_LAUNCHER (ret),
172                                                runtime_id + strlen ("flatpak:"));
173     }
174   else if (ret != NULL)
175     {
176       g_autofree gchar *build_path = NULL;
177       g_autofree gchar *ccache_dir = NULL;
178       g_auto(GStrv) new_environ = NULL;
179       const gchar *builddir = NULL;
180       const gchar *project_path = NULL;
181       const gchar * const *build_args = NULL;
182       const gchar *config_dir = gbp_flatpak_get_config_dir ();
183       g_autoptr(IdeConfigManager) config_manager = NULL;
184       IdeConfig *configuration;
185       IdeVcs *vcs;
186 
187       config_manager = ide_config_manager_ref_from_context (context);
188       configuration = ide_config_manager_ref_current (config_manager);
189 
190       build_path = get_staging_directory (self);
191       builddir = get_builddir (self);
192 
193       /* Find the project directory path */
194       vcs = ide_vcs_ref_from_context (context);
195       project_path = g_file_peek_path (ide_vcs_get_workdir (vcs));
196 
197       /* Add 'flatpak build' and the specified arguments to the launcher */
198       ide_subprocess_launcher_push_argv (ret, "flatpak");
199       ide_subprocess_launcher_push_argv (ret, "build");
200 
201       /* Get access to override installations */
202       ide_subprocess_launcher_setenv (ret, "FLATPAK_CONFIG_DIR", config_dir, TRUE);
203 
204       if (GBP_IS_FLATPAK_MANIFEST (configuration))
205         build_args = gbp_flatpak_manifest_get_build_args (GBP_FLATPAK_MANIFEST (configuration));
206 
207       if (build_args != NULL)
208         ide_subprocess_launcher_push_args (ret, build_args);
209       else
210         ide_subprocess_launcher_push_argv (ret, "--share=network");
211 
212       /* We might need access to ccache configs inside the build environ.
213        * Usually, this is set by flatpak-builder, but since we are running
214        * the incremental build, we need to take care of that too.
215        *
216        * See https://bugzilla.gnome.org/show_bug.cgi?id=780153
217        */
218       ccache_dir = g_build_filename (g_get_user_cache_dir (),
219                                      ide_get_program_name (),
220                                      "flatpak-builder",
221                                      "ccache",
222                                      NULL);
223       ide_subprocess_launcher_setenv (ret, "CCACHE_DIR", ccache_dir, FALSE);
224 
225       if (!ide_str_empty0 (project_path))
226         {
227           g_autofree gchar *filesystem_option_src = NULL;
228           g_autofree gchar *filesystem_option_build = NULL;
229           g_autofree gchar *filesystem_option_cache = NULL;
230 
231           filesystem_option_src = g_strdup_printf ("--filesystem=%s", project_path);
232           filesystem_option_build = g_strdup_printf ("--filesystem=%s", builddir);
233           filesystem_option_cache = g_strdup_printf ("--filesystem=%s/gnome-builder", g_get_user_cache_dir ());
234           ide_subprocess_launcher_push_argv (ret, "--nofilesystem=host");
235           ide_subprocess_launcher_push_argv (ret, filesystem_option_cache);
236           ide_subprocess_launcher_push_argv (ret, filesystem_option_src);
237           ide_subprocess_launcher_push_argv (ret, filesystem_option_build);
238         }
239 
240       new_environ = ide_config_get_environ (IDE_CONFIG (configuration));
241 
242       if (!strv_empty (new_environ))
243         {
244           for (guint i = 0; new_environ[i]; i++)
245             {
246               if (g_utf8_strlen (new_environ[i], -1) > 1)
247                 {
248                   g_autofree gchar *env_option = NULL;
249 
250                   env_option = g_strdup_printf ("--env=%s", new_environ[i]);
251                   ide_subprocess_launcher_push_argv (ret, env_option);
252                 }
253             }
254         }
255 
256       /* We want the configure step to be separate so IdeAutotoolsBuildTask can pass options to it */
257       ide_subprocess_launcher_push_argv (ret, "--env=NOCONFIGURE=1");
258 
259       ide_subprocess_launcher_push_argv (ret, build_path);
260 
261       ide_subprocess_launcher_set_run_on_host (ret, TRUE);
262     }
263 
264   return ret;
265 }
266 
267 static gchar *
get_manifst_command(GbpFlatpakRuntime * self)268 get_manifst_command (GbpFlatpakRuntime *self)
269 {
270   IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
271   IdeConfigManager *config_manager = ide_config_manager_from_context (context);
272   IdeConfig *config = ide_config_manager_get_current (config_manager);
273 
274   if (GBP_IS_FLATPAK_MANIFEST (config))
275     {
276       const gchar *command;
277 
278       command = gbp_flatpak_manifest_get_command (GBP_FLATPAK_MANIFEST (config));
279       if (!ide_str_empty0 (command))
280         return g_strdup (command);
281     }
282 
283   /* Use the project id as a last resort */
284   return ide_context_dup_project_id (context);
285 }
286 
287 static IdeRunner *
gbp_flatpak_runtime_create_runner(IdeRuntime * runtime,IdeBuildTarget * build_target)288 gbp_flatpak_runtime_create_runner (IdeRuntime     *runtime,
289                                    IdeBuildTarget *build_target)
290 {
291   GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)runtime;
292   g_autofree gchar *build_path = NULL;
293   g_autofree gchar *binary_name = NULL;
294   IdeContext *context;
295   IdeRunner *runner;
296 
297   g_assert (GBP_IS_FLATPAK_RUNTIME (self));
298   g_assert (!build_target || IDE_IS_BUILD_TARGET (build_target));
299 
300   context = ide_object_get_context (IDE_OBJECT (self));
301   build_path = get_staging_directory (self);
302 
303   binary_name = get_manifst_command (self);
304 
305   if ((runner = IDE_RUNNER (gbp_flatpak_runner_new (context, build_path, build_target, binary_name))))
306     ide_object_append (IDE_OBJECT (self), IDE_OBJECT (runner));
307 
308   return runner;
309 }
310 
311 static void
gbp_flatpak_runtime_prepare_configuration(IdeRuntime * runtime,IdeConfig * config)312 gbp_flatpak_runtime_prepare_configuration (IdeRuntime *runtime,
313                                            IdeConfig  *config)
314 {
315   g_assert (GBP_IS_FLATPAK_RUNTIME (runtime));
316   g_assert (IDE_IS_CONFIG (config));
317 
318   ide_config_set_prefix (config, "/app");
319   ide_config_set_prefix_set (config, FALSE);
320 }
321 
322 static void
gbp_flatpak_runtime_set_deploy_dir(GbpFlatpakRuntime * self,const gchar * deploy_dir)323 gbp_flatpak_runtime_set_deploy_dir (GbpFlatpakRuntime *self,
324                                     const gchar       *deploy_dir)
325 {
326   g_autoptr(GFile) file = NULL;
327 
328   g_assert (GBP_IS_FLATPAK_RUNTIME (self));
329   g_assert (self->deploy_dir == NULL);
330   g_assert (self->deploy_dir_files == NULL);
331 
332   if (deploy_dir != NULL)
333     {
334       self->deploy_dir = g_strdup (deploy_dir);
335       file = g_file_new_for_path (deploy_dir);
336       self->deploy_dir_files = g_file_get_child (file, "files");
337     }
338 }
339 
340 static const gchar *
gbp_flatpak_runtime_get_runtime_dir(GbpFlatpakRuntime * self)341 gbp_flatpak_runtime_get_runtime_dir (GbpFlatpakRuntime *self)
342 {
343   if G_UNLIKELY (self->runtime_dir == NULL)
344     {
345       g_autofree gchar *sdk_name = NULL;
346       const gchar *ids[2];
347 
348       sdk_name = gbp_flatpak_runtime_get_sdk_name (self);
349 
350       ids[0] = self->platform;
351       ids[1] = sdk_name;
352 
353       for (guint i = 0; i < G_N_ELEMENTS (ids); i++)
354         {
355           const gchar *id = ids[i];
356           g_autofree gchar *name = g_strdup_printf ("%s.Debug", id);
357           g_autofree gchar *deploy_path = NULL;
358           g_autofree gchar *path = NULL;
359 
360           /*
361            * The easiest way to reliably stay within the same installation
362            * is to just use relative paths to the checkout of the deploy dir.
363            */
364           deploy_path = g_file_get_path (self->deploy_dir_files);
365           path = g_build_filename (deploy_path, "..", "..", "..", "..", "..",
366                                    name, ide_triplet_get_arch (self->triplet),
367                                    self->branch, "active", "files",
368                                    NULL);
369           if (g_file_test (path, G_FILE_TEST_IS_DIR))
370             {
371               self->runtime_dir = g_steal_pointer (&path);
372               break;
373             }
374         }
375     }
376 
377   return self->runtime_dir;
378 }
379 
380 static GFile *
gbp_flatpak_runtime_translate_file(IdeRuntime * runtime,GFile * file)381 gbp_flatpak_runtime_translate_file (IdeRuntime *runtime,
382                                     GFile      *file)
383 {
384   GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)runtime;
385   const gchar *runtime_dir;
386   g_autofree gchar *path = NULL;
387   g_autofree gchar *build_dir = NULL;
388   g_autofree gchar *app_files_path = NULL;
389   g_autofree gchar *debug_dir = NULL;
390 
391   g_assert (GBP_IS_FLATPAK_RUNTIME (self));
392   g_assert (G_IS_FILE (file));
393 
394   /*
395    * If we have a manifest and the runtime is not yet installed,
396    * then we can't do a whole lot right now. We have to wait for
397    * the runtime to be installed and a new runtime instance will
398    * be loaded.
399    */
400   if (self->deploy_dir_files == NULL || self->deploy_dir == NULL)
401     return NULL;
402 
403   if (!g_file_is_native (file))
404     return NULL;
405 
406   if (NULL == (path = g_file_get_path (file)))
407     return NULL;
408 
409   runtime_dir = gbp_flatpak_runtime_get_runtime_dir (self);
410 
411   if (runtime_dir != NULL)
412     {
413       if (g_str_has_prefix (path, "/run/build-runtime/"))
414         {
415           g_autofree gchar *translated = NULL;
416 
417           translated = g_build_filename (runtime_dir,
418                                          "source",
419                                          path + DZL_LITERAL_LENGTH ("/run/build-runtime/"),
420                                          NULL);
421           return g_file_new_for_path (translated);
422         }
423 
424       debug_dir = g_build_filename (runtime_dir, "usr", "lib", NULL);
425 
426       if (g_str_equal (path, "/usr/lib/debug") ||
427           g_str_equal (path, "/usr/lib/debug/"))
428         return g_file_new_for_path (debug_dir);
429 
430       if (g_str_has_prefix (path, "/usr/lib/debug/"))
431         {
432           g_autofree gchar *translated = NULL;
433 
434           translated = g_build_filename (debug_dir,
435                                          path + DZL_LITERAL_LENGTH ("/usr/lib/debug/"),
436                                          NULL);
437           return g_file_new_for_path (translated);
438         }
439     }
440 
441   if (g_str_equal ("/usr", path))
442     return g_object_ref (self->deploy_dir_files);
443 
444   if (g_str_has_prefix (path, "/usr/"))
445     return g_file_get_child (self->deploy_dir_files, path + DZL_LITERAL_LENGTH ("/usr/"));
446 
447   build_dir = get_staging_directory (self);
448   app_files_path = g_build_filename (build_dir, "files", NULL);
449 
450   if (g_str_equal (path, "/app") || g_str_equal (path, "/app/"))
451     return g_file_new_for_path (app_files_path);
452 
453   if (g_str_has_prefix (path, "/app/"))
454     {
455       g_autofree gchar *translated = NULL;
456 
457       translated = g_build_filename (app_files_path,
458                                      path + DZL_LITERAL_LENGTH ("/app/"),
459                                      NULL);
460       return g_file_new_for_path (translated);
461     }
462 
463   return NULL;
464 }
465 
466 IdeTriplet *
gbp_flatpak_runtime_get_triplet(GbpFlatpakRuntime * self)467 gbp_flatpak_runtime_get_triplet (GbpFlatpakRuntime *self)
468 {
469   g_return_val_if_fail (GBP_IS_FLATPAK_RUNTIME (self), NULL);
470 
471   return self->triplet;
472 }
473 
474 static void
gbp_flatpak_runtime_set_triplet(GbpFlatpakRuntime * self,IdeTriplet * triplet)475 gbp_flatpak_runtime_set_triplet (GbpFlatpakRuntime *self,
476                                  IdeTriplet        *triplet)
477 {
478   g_return_if_fail (GBP_IS_FLATPAK_RUNTIME (self));
479 
480   if (self->triplet != triplet)
481     {
482       g_clear_pointer (&self->triplet, ide_triplet_unref);
483       self->triplet = ide_triplet_ref (triplet);
484       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TRIPLET]);
485     }
486 }
487 
488 const gchar *
gbp_flatpak_runtime_get_branch(GbpFlatpakRuntime * self)489 gbp_flatpak_runtime_get_branch (GbpFlatpakRuntime *self)
490 {
491   g_return_val_if_fail (GBP_IS_FLATPAK_RUNTIME (self), NULL);
492 
493   return self->branch;
494 }
495 
496 static void
gbp_flatpak_runtime_set_branch(GbpFlatpakRuntime * self,const gchar * branch)497 gbp_flatpak_runtime_set_branch (GbpFlatpakRuntime *self,
498                                 const gchar       *branch)
499 {
500   g_return_if_fail (GBP_IS_FLATPAK_RUNTIME (self));
501 
502   if (g_strcmp0 (branch, self->branch) != 0)
503     {
504       g_free (self->branch);
505       self->branch = g_strdup (branch);
506       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BRANCH]);
507     }
508 }
509 
510 const gchar *
gbp_flatpak_runtime_get_platform(GbpFlatpakRuntime * self)511 gbp_flatpak_runtime_get_platform (GbpFlatpakRuntime *self)
512 {
513   g_return_val_if_fail (GBP_IS_FLATPAK_RUNTIME (self), NULL);
514 
515   return self->platform;
516 }
517 
518 static void
gbp_flatpak_runtime_set_platform(GbpFlatpakRuntime * self,const gchar * platform)519 gbp_flatpak_runtime_set_platform (GbpFlatpakRuntime *self,
520                                   const gchar       *platform)
521 {
522   g_return_if_fail (GBP_IS_FLATPAK_RUNTIME (self));
523 
524   g_free (self->platform);
525   self->platform = g_strdup (platform);
526   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PLATFORM]);
527 }
528 
529 const gchar *
gbp_flatpak_runtime_get_sdk(GbpFlatpakRuntime * self)530 gbp_flatpak_runtime_get_sdk (GbpFlatpakRuntime *self)
531 {
532   g_return_val_if_fail (GBP_IS_FLATPAK_RUNTIME (self), NULL);
533 
534   return self->sdk;
535 }
536 
537 gchar *
gbp_flatpak_runtime_get_sdk_name(GbpFlatpakRuntime * self)538 gbp_flatpak_runtime_get_sdk_name (GbpFlatpakRuntime *self)
539 {
540   const gchar *slash;
541 
542   g_return_val_if_fail (GBP_IS_FLATPAK_RUNTIME (self), NULL);
543 
544   if (self->sdk == NULL)
545     return NULL;
546 
547   slash = strchr (self->sdk, '/');
548 
549   if (slash == NULL)
550     return g_strdup (self->sdk);
551   else
552     return g_strndup (self->sdk, slash - self->sdk);
553 }
554 
555 static void
gbp_flatpak_runtime_set_sdk(GbpFlatpakRuntime * self,const gchar * sdk)556 gbp_flatpak_runtime_set_sdk (GbpFlatpakRuntime *self,
557                              const gchar       *sdk)
558 {
559   g_return_if_fail (GBP_IS_FLATPAK_RUNTIME (self));
560 
561   if (g_strcmp0 (sdk, self->sdk) != 0)
562     {
563       g_free (self->sdk);
564       self->sdk = g_strdup (sdk);
565       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SDK]);
566     }
567 }
568 
569 static gchar **
gbp_flatpak_runtime_get_system_include_dirs(IdeRuntime * runtime)570 gbp_flatpak_runtime_get_system_include_dirs (IdeRuntime *runtime)
571 {
572   static const gchar *include_dirs[] = { "/app/include", "/usr/include", NULL };
573   return g_strdupv ((gchar **)include_dirs);
574 }
575 
576 static IdeTriplet *
gbp_flatpak_runtime_real_get_triplet(IdeRuntime * runtime)577 gbp_flatpak_runtime_real_get_triplet (IdeRuntime *runtime)
578 {
579   return ide_triplet_ref (GBP_FLATPAK_RUNTIME (runtime)->triplet);
580 }
581 
582 static gboolean
gbp_flatpak_runtime_supports_toolchain(IdeRuntime * self,IdeToolchain * toolchain)583 gbp_flatpak_runtime_supports_toolchain (IdeRuntime   *self,
584                                         IdeToolchain *toolchain)
585 {
586   return FALSE;
587 }
588 
589 static void
gbp_flatpak_runtime_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)590 gbp_flatpak_runtime_get_property (GObject    *object,
591                                   guint       prop_id,
592                                   GValue     *value,
593                                   GParamSpec *pspec)
594 {
595   GbpFlatpakRuntime *self = GBP_FLATPAK_RUNTIME(object);
596 
597   switch (prop_id)
598     {
599     case PROP_TRIPLET:
600       g_value_set_boxed (value, gbp_flatpak_runtime_get_triplet (self));
601       break;
602 
603     case PROP_BRANCH:
604       g_value_set_string (value, gbp_flatpak_runtime_get_branch (self));
605       break;
606 
607     case PROP_PLATFORM:
608       g_value_set_string (value, gbp_flatpak_runtime_get_platform (self));
609       break;
610 
611     case PROP_SDK:
612       g_value_set_string (value, gbp_flatpak_runtime_get_sdk (self));
613       break;
614 
615     case PROP_DEPLOY_DIR:
616       g_value_set_string (value, self->deploy_dir);
617       break;
618 
619     default:
620       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
621     }
622 }
623 
624 static void
gbp_flatpak_runtime_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)625 gbp_flatpak_runtime_set_property (GObject      *object,
626                                   guint         prop_id,
627                                   const GValue *value,
628                                   GParamSpec   *pspec)
629 {
630   GbpFlatpakRuntime *self = GBP_FLATPAK_RUNTIME(object);
631 
632   switch (prop_id)
633     {
634     case PROP_TRIPLET:
635       gbp_flatpak_runtime_set_triplet (self, g_value_get_boxed (value));
636       break;
637 
638     case PROP_BRANCH:
639       gbp_flatpak_runtime_set_branch (self, g_value_get_string (value));
640       break;
641 
642     case PROP_PLATFORM:
643       gbp_flatpak_runtime_set_platform (self, g_value_get_string (value));
644       break;
645 
646     case PROP_SDK:
647       gbp_flatpak_runtime_set_sdk (self, g_value_get_string (value));
648       break;
649 
650     case PROP_DEPLOY_DIR:
651       gbp_flatpak_runtime_set_deploy_dir (self, g_value_get_string (value));
652       break;
653 
654     default:
655       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
656     }
657 }
658 
659 static void
gbp_flatpak_runtime_finalize(GObject * object)660 gbp_flatpak_runtime_finalize (GObject *object)
661 {
662   GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)object;
663 
664   g_clear_pointer (&self->triplet, ide_triplet_unref);
665   g_clear_pointer (&self->branch, g_free);
666   g_clear_pointer (&self->runtime_dir, g_free);
667   g_clear_pointer (&self->deploy_dir, g_free);
668   g_clear_pointer (&self->platform, g_free);
669   g_clear_pointer (&self->sdk, g_free);
670   g_clear_pointer (&self->program_paths_cache, g_hash_table_unref);
671   g_clear_object (&self->deploy_dir_files);
672 
673   G_OBJECT_CLASS (gbp_flatpak_runtime_parent_class)->finalize (object);
674 }
675 
676 static void
gbp_flatpak_runtime_class_init(GbpFlatpakRuntimeClass * klass)677 gbp_flatpak_runtime_class_init (GbpFlatpakRuntimeClass *klass)
678 {
679   GObjectClass *object_class = G_OBJECT_CLASS (klass);
680   IdeRuntimeClass *runtime_class = IDE_RUNTIME_CLASS (klass);
681 
682   object_class->finalize = gbp_flatpak_runtime_finalize;
683   object_class->get_property = gbp_flatpak_runtime_get_property;
684   object_class->set_property = gbp_flatpak_runtime_set_property;
685 
686   runtime_class->create_launcher = gbp_flatpak_runtime_create_launcher;
687   runtime_class->create_runner = gbp_flatpak_runtime_create_runner;
688   runtime_class->contains_program_in_path = gbp_flatpak_runtime_contains_program_in_path;
689   runtime_class->prepare_configuration = gbp_flatpak_runtime_prepare_configuration;
690   runtime_class->translate_file = gbp_flatpak_runtime_translate_file;
691   runtime_class->get_system_include_dirs = gbp_flatpak_runtime_get_system_include_dirs;
692   runtime_class->get_triplet = gbp_flatpak_runtime_real_get_triplet;
693   runtime_class->supports_toolchain = gbp_flatpak_runtime_supports_toolchain;
694 
695   properties [PROP_TRIPLET] =
696     g_param_spec_boxed ("triplet",
697                          "Triplet",
698                          "Architecture Triplet",
699                          IDE_TYPE_TRIPLET,
700                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
701 
702   properties [PROP_BRANCH] =
703     g_param_spec_string ("branch",
704                          "Branch",
705                          "Branch",
706                          "master",
707                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
708 
709   properties [PROP_DEPLOY_DIR] =
710     g_param_spec_string ("deploy-dir",
711                          "Deploy Directory",
712                          "The flatpak runtime deploy directory",
713                          NULL,
714                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
715 
716   properties [PROP_PLATFORM] =
717     g_param_spec_string ("platform",
718                          "Platform",
719                          "Platform",
720                          "org.gnome.Platform",
721                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
722 
723   properties [PROP_SDK] =
724     g_param_spec_string ("sdk",
725                          "Sdk",
726                          "Sdk",
727                          "org.gnome.Sdk",
728                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
729 
730   g_object_class_install_properties (object_class, N_PROPS, properties);
731 }
732 
733 static void
gbp_flatpak_runtime_init(GbpFlatpakRuntime * self)734 gbp_flatpak_runtime_init (GbpFlatpakRuntime *self)
735 {
736   self->program_paths_cache = g_hash_table_new (g_str_hash, g_str_equal);
737 }
738 
739 GbpFlatpakRuntime *
gbp_flatpak_runtime_new(const char * name,const char * arch,const char * branch,const char * sdk_name,const char * sdk_branch,const char * deploy_dir,const char * metadata,gboolean is_extension)740 gbp_flatpak_runtime_new (const char *name,
741                          const char *arch,
742                          const char *branch,
743                          const char *sdk_name,
744                          const char *sdk_branch,
745                          const char *deploy_dir,
746                          const char *metadata,
747                          gboolean    is_extension)
748 {
749   g_autofree gchar *id = NULL;
750   g_autofree gchar *short_id = NULL;
751   g_autofree gchar *display_name = NULL;
752   g_autofree gchar *triplet = NULL;
753   g_autofree gchar *runtime_name = NULL;
754   g_autoptr(IdeTriplet) triplet_object = NULL;
755   g_autoptr(GString) category = NULL;
756 
757   g_return_val_if_fail (name != NULL, NULL);
758   g_return_val_if_fail (arch != NULL, NULL);
759   g_return_val_if_fail (branch != NULL, NULL);
760   g_return_val_if_fail (deploy_dir != NULL, NULL);
761 
762   if (sdk_name == NULL)
763     sdk_name = name;
764 
765   if (sdk_branch == NULL)
766     sdk_branch = branch;
767 
768   triplet_object = ide_triplet_new (arch);
769   triplet = g_strdup_printf ("%s/%s/%s", name, arch, branch);
770   id = g_strdup_printf ("flatpak:%s", triplet);
771   short_id = g_strdup_printf ("flatpak:%s-%s", name, branch);
772 
773   category = g_string_new ("Flatpak/");
774 
775   if (g_str_has_prefix (name, "org.gnome."))
776     g_string_append (category, "GNOME/");
777   else if (g_str_has_prefix (name, "org.freedesktop."))
778     g_string_append (category, "FreeDesktop.org/");
779   else if (g_str_has_prefix (name, "org.kde."))
780     g_string_append (category, "KDE/");
781 
782   if (ide_str_equal0 (ide_get_system_arch (), arch))
783     g_string_append (category, name);
784   else
785     g_string_append_printf (category, "%s (%s)", name, arch);
786 
787   if (g_str_equal (arch, ide_get_system_arch ()))
788     display_name = g_strdup_printf (_("%s <b>%s</b>"), name, branch);
789   else
790     display_name = g_strdup_printf (_("%s <b>%s</b> <span fgalpha='36044'>%s</span>"), name, branch, arch);
791 
792   runtime_name = g_strdup_printf ("%s %s", _("Flatpak"), triplet);
793 
794   return g_object_new (GBP_TYPE_FLATPAK_RUNTIME,
795                        "id", id,
796                        "short-id", short_id,
797                        "triplet", triplet_object,
798                        "branch", branch,
799                        "category", category->str,
800                        "name", runtime_name,
801                        "deploy-dir", deploy_dir,
802                        "display-name", display_name,
803                        "platform", name,
804                        "sdk", sdk_name,
805                        NULL);
806 }
807 
808 char **
gbp_flatpak_runtime_get_refs(GbpFlatpakRuntime * self)809 gbp_flatpak_runtime_get_refs (GbpFlatpakRuntime *self)
810 {
811   GPtrArray *ar;
812   g_autofree char *sdk = NULL;
813   g_autofree char *platform = NULL;
814   const char *arch;
815 
816   g_return_val_if_fail (GBP_IS_FLATPAK_RUNTIME (self), NULL);
817 
818   arch = ide_triplet_get_arch (self->triplet);
819   platform = g_strdup_printf ("runtime/%s/%s/%s", self->platform, arch, self->branch);
820   sdk = g_strdup_printf ("runtime/%s/%s/%s", self->sdk, arch, self->branch);
821 
822   ar = g_ptr_array_new ();
823   g_ptr_array_add (ar, g_steal_pointer (&sdk));
824   if (g_strcmp0 (sdk, platform) != 0)
825     g_ptr_array_add (ar, g_steal_pointer (&platform));
826   g_ptr_array_add (ar, NULL);
827 
828   return (char **)g_ptr_array_free (ar, FALSE);
829 }
830