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