1 /*
2 * This file is part of libmodulemd
3 * Copyright (C) 2018 Red Hat, Inc.
4 *
5 * Fedora-License-Identifier: MIT
6 * SPDX-2.0-License-Identifier: MIT
7 * SPDX-3.0-License-Identifier: MIT
8 *
9 * This program is free software.
10 * For more information on the license, see COPYING.
11 * For more information on free software, see <https://www.gnu.org/philosophy/free-sw.en.html>.
12 */
13
14 #include <glib.h>
15 #include <yaml.h>
16
17 #include "modulemd-buildopts.h"
18 #include "private/glib-extensions.h"
19 #include "private/modulemd-buildopts-private.h"
20 #include "private/modulemd-util.h"
21 #include "private/modulemd-yaml.h"
22
23 #define B_DEFAULT_STRING "__BUILDOPTS_RPM_MACROS_UNSET__"
24
25 struct _ModulemdBuildopts
26 {
27 GObject parent_instance;
28
29 gchar *rpm_macros;
30
31 GHashTable *whitelist;
32 GHashTable *arches;
33 };
34
35 G_DEFINE_TYPE (ModulemdBuildopts, modulemd_buildopts, G_TYPE_OBJECT)
36
37 enum
38 {
39 PROP_0,
40
41 PROP_RPM_MACROS,
42
43 N_PROPS
44 };
45
46 static GParamSpec *properties[N_PROPS];
47
48
49 gboolean
modulemd_buildopts_equals(ModulemdBuildopts * self_1,ModulemdBuildopts * self_2)50 modulemd_buildopts_equals (ModulemdBuildopts *self_1,
51 ModulemdBuildopts *self_2)
52 {
53 if (!self_1 && !self_2)
54 {
55 return TRUE;
56 }
57
58 if (!self_1 || !self_2)
59 {
60 return FALSE;
61 }
62
63 g_return_val_if_fail (MODULEMD_IS_BUILDOPTS (self_1), FALSE);
64 g_return_val_if_fail (MODULEMD_IS_BUILDOPTS (self_2), FALSE);
65
66 if (g_strcmp0 (self_1->rpm_macros, self_2->rpm_macros) != 0)
67 {
68 return FALSE;
69 }
70
71 if (!modulemd_hash_table_sets_are_equal (self_1->whitelist,
72 self_2->whitelist))
73 {
74 return FALSE;
75 }
76
77 if (!modulemd_hash_table_sets_are_equal (self_1->arches, self_2->arches))
78 {
79 return FALSE;
80 }
81
82 return TRUE;
83 }
84
85 gint
modulemd_buildopts_compare(ModulemdBuildopts * self_1,ModulemdBuildopts * self_2)86 modulemd_buildopts_compare (ModulemdBuildopts *self_1,
87 ModulemdBuildopts *self_2)
88 {
89 gint cmp;
90
91 if (!self_1 && !self_2)
92 {
93 return 0;
94 }
95
96 if (!self_1)
97 {
98 return -1;
99 }
100
101 if (!self_2)
102 {
103 return 1;
104 }
105
106 g_return_val_if_fail (MODULEMD_IS_BUILDOPTS (self_1), 1);
107 g_return_val_if_fail (MODULEMD_IS_BUILDOPTS (self_2), -1);
108
109 cmp = g_strcmp0 (self_1->rpm_macros, self_2->rpm_macros);
110 if (cmp != 0)
111 {
112 return cmp;
113 }
114
115 cmp =
116 modulemd_hash_table_compare (self_1->whitelist, self_2->whitelist, NULL);
117 if (cmp != 0)
118 {
119 return cmp;
120 }
121
122 cmp = modulemd_hash_table_compare (self_1->arches, self_2->arches, NULL);
123 if (cmp != 0)
124 {
125 return cmp;
126 }
127
128 return 0;
129 }
130
131
132 ModulemdBuildopts *
modulemd_buildopts_new(void)133 modulemd_buildopts_new (void)
134 {
135 return g_object_new (MODULEMD_TYPE_BUILDOPTS, NULL);
136 }
137
138
139 ModulemdBuildopts *
modulemd_buildopts_copy(ModulemdBuildopts * self)140 modulemd_buildopts_copy (ModulemdBuildopts *self)
141 {
142 g_autoptr (ModulemdBuildopts) copy = NULL;
143 g_return_val_if_fail (MODULEMD_IS_BUILDOPTS (self), NULL);
144
145 copy = modulemd_buildopts_new ();
146
147 modulemd_buildopts_set_rpm_macros (copy,
148 modulemd_buildopts_get_rpm_macros (self));
149
150 MODULEMD_REPLACE_SET (copy->whitelist, self->whitelist);
151 MODULEMD_REPLACE_SET (copy->arches, self->arches);
152
153 return g_steal_pointer (©);
154 }
155
156
157 static void
modulemd_buildopts_finalize(GObject * object)158 modulemd_buildopts_finalize (GObject *object)
159 {
160 ModulemdBuildopts *self = (ModulemdBuildopts *)object;
161
162 g_clear_pointer (&self->rpm_macros, g_free);
163 g_clear_pointer (&self->whitelist, g_hash_table_unref);
164 g_clear_pointer (&self->arches, g_hash_table_unref);
165
166 G_OBJECT_CLASS (modulemd_buildopts_parent_class)->finalize (object);
167 }
168
169
170 void
modulemd_buildopts_set_rpm_macros(ModulemdBuildopts * self,const gchar * rpm_macros)171 modulemd_buildopts_set_rpm_macros (ModulemdBuildopts *self,
172 const gchar *rpm_macros)
173 {
174 g_return_if_fail (MODULEMD_IS_BUILDOPTS (self));
175
176 g_clear_pointer (&self->rpm_macros, g_free);
177 self->rpm_macros = g_strdup (rpm_macros);
178
179 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RPM_MACROS]);
180 }
181
182
183 const gchar *
modulemd_buildopts_get_rpm_macros(ModulemdBuildopts * self)184 modulemd_buildopts_get_rpm_macros (ModulemdBuildopts *self)
185 {
186 g_return_val_if_fail (MODULEMD_IS_BUILDOPTS (self), NULL);
187
188 return self->rpm_macros;
189 }
190
191
192 void
modulemd_buildopts_add_rpm_to_whitelist(ModulemdBuildopts * self,const gchar * rpm)193 modulemd_buildopts_add_rpm_to_whitelist (ModulemdBuildopts *self,
194 const gchar *rpm)
195 {
196 g_return_if_fail (MODULEMD_IS_BUILDOPTS (self));
197 g_hash_table_add (self->whitelist, g_strdup (rpm));
198 }
199
200
201 void
modulemd_buildopts_remove_rpm_from_whitelist(ModulemdBuildopts * self,const gchar * rpm)202 modulemd_buildopts_remove_rpm_from_whitelist (ModulemdBuildopts *self,
203 const gchar *rpm)
204 {
205 g_return_if_fail (MODULEMD_IS_BUILDOPTS (self));
206 g_hash_table_remove (self->whitelist, rpm);
207 }
208
209 void
modulemd_buildopts_clear_rpm_whitelist(ModulemdBuildopts * self)210 modulemd_buildopts_clear_rpm_whitelist (ModulemdBuildopts *self)
211 {
212 g_return_if_fail (MODULEMD_IS_BUILDOPTS (self));
213 g_hash_table_remove_all (self->whitelist);
214 }
215
216
217 GStrv
modulemd_buildopts_get_rpm_whitelist_as_strv(ModulemdBuildopts * self)218 modulemd_buildopts_get_rpm_whitelist_as_strv (ModulemdBuildopts *self)
219 {
220 g_return_val_if_fail (MODULEMD_IS_BUILDOPTS (self), NULL);
221
222 return modulemd_ordered_str_keys_as_strv (self->whitelist);
223 }
224
225
226 void
modulemd_buildopts_add_arch(ModulemdBuildopts * self,const gchar * arch)227 modulemd_buildopts_add_arch (ModulemdBuildopts *self, const gchar *arch)
228 {
229 g_return_if_fail (MODULEMD_IS_BUILDOPTS (self));
230 g_hash_table_add (self->arches, g_strdup (arch));
231 }
232
233
234 void
modulemd_buildopts_remove_arch(ModulemdBuildopts * self,const gchar * arch)235 modulemd_buildopts_remove_arch (ModulemdBuildopts *self, const gchar *arch)
236 {
237 g_return_if_fail (MODULEMD_IS_BUILDOPTS (self));
238 g_hash_table_remove (self->arches, arch);
239 }
240
241
242 void
modulemd_buildopts_clear_arches(ModulemdBuildopts * self)243 modulemd_buildopts_clear_arches (ModulemdBuildopts *self)
244 {
245 g_return_if_fail (MODULEMD_IS_BUILDOPTS (self));
246 g_hash_table_remove_all (self->arches);
247 }
248
249
250 GStrv
modulemd_buildopts_get_arches_as_strv(ModulemdBuildopts * self)251 modulemd_buildopts_get_arches_as_strv (ModulemdBuildopts *self)
252 {
253 g_return_val_if_fail (MODULEMD_IS_BUILDOPTS (self), NULL);
254
255 return modulemd_ordered_str_keys_as_strv (self->arches);
256 }
257
258
259 static void
modulemd_buildopts_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)260 modulemd_buildopts_get_property (GObject *object,
261 guint prop_id,
262 GValue *value,
263 GParamSpec *pspec)
264 {
265 ModulemdBuildopts *self = MODULEMD_BUILDOPTS (object);
266
267 switch (prop_id)
268 {
269 case PROP_RPM_MACROS:
270 g_value_set_string (value, modulemd_buildopts_get_rpm_macros (self));
271 break;
272 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
273 }
274 }
275
276
277 static void
modulemd_buildopts_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)278 modulemd_buildopts_set_property (GObject *object,
279 guint prop_id,
280 const GValue *value,
281 GParamSpec *pspec)
282 {
283 ModulemdBuildopts *self = MODULEMD_BUILDOPTS (object);
284
285 switch (prop_id)
286 {
287 case PROP_RPM_MACROS:
288 modulemd_buildopts_set_rpm_macros (self, g_value_get_string (value));
289 break;
290 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
291 }
292 }
293
294
295 static void
modulemd_buildopts_class_init(ModulemdBuildoptsClass * klass)296 modulemd_buildopts_class_init (ModulemdBuildoptsClass *klass)
297 {
298 GObjectClass *object_class = G_OBJECT_CLASS (klass);
299
300 object_class->finalize = modulemd_buildopts_finalize;
301 object_class->get_property = modulemd_buildopts_get_property;
302 object_class->set_property = modulemd_buildopts_set_property;
303
304 properties[PROP_RPM_MACROS] = g_param_spec_string (
305 "rpm-macros",
306 "Rpm Macros",
307 "A string containing RPM build macros in the form that they would appear "
308 "in an RPM macros file on-disk.",
309 B_DEFAULT_STRING,
310 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
311
312 g_object_class_install_properties (object_class, N_PROPS, properties);
313 }
314
315
316 static void
modulemd_buildopts_init(ModulemdBuildopts * self)317 modulemd_buildopts_init (ModulemdBuildopts *self)
318 {
319 self->whitelist =
320 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
321 self->arches = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
322 }
323
324
325 /* === YAML Functions === */
326
327 static gboolean
328 modulemd_buildopts_parse_rpm_buildopts (yaml_parser_t *parser,
329 ModulemdBuildopts *buildopts,
330 gboolean strict,
331 GError **error);
332
333 ModulemdBuildopts *
modulemd_buildopts_parse_yaml(yaml_parser_t * parser,gboolean strict,GError ** error)334 modulemd_buildopts_parse_yaml (yaml_parser_t *parser,
335 gboolean strict,
336 GError **error)
337 {
338 MODULEMD_INIT_TRACE ();
339 MMD_INIT_YAML_EVENT (event);
340 gboolean done = FALSE;
341 gboolean in_map = FALSE;
342 g_autoptr (ModulemdBuildopts) buildopts = NULL;
343 g_autoptr (GError) nested_error = NULL;
344
345 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
346
347 buildopts = modulemd_buildopts_new ();
348
349 while (!done)
350 {
351 YAML_PARSER_PARSE_WITH_EXIT (parser, &event, error);
352
353 switch (event.type)
354 {
355 case YAML_MAPPING_START_EVENT: in_map = TRUE; break;
356
357 case YAML_MAPPING_END_EVENT:
358 in_map = FALSE;
359 done = TRUE;
360 break;
361
362 case YAML_SCALAR_EVENT:
363 if (!in_map)
364 {
365 MMD_YAML_ERROR_EVENT_EXIT_BOOL (
366 error, event, "Missing mapping in buildopts");
367 }
368
369 if (g_str_equal ((const gchar *)event.data.scalar.value, "rpms"))
370 {
371 if (!modulemd_buildopts_parse_rpm_buildopts (
372 parser, buildopts, strict, &nested_error))
373 {
374 g_propagate_error (error, nested_error);
375 return NULL;
376 }
377 }
378 else if (g_str_equal (event.data.scalar.value, "arches"))
379 {
380 g_hash_table_unref (buildopts->arches);
381 buildopts->arches =
382 modulemd_yaml_parse_string_set (parser, &nested_error);
383 if (buildopts->arches == NULL)
384 {
385 MMD_YAML_ERROR_EVENT_EXIT_BOOL (
386 error,
387 event,
388 "Failed to parse arches list in buildopts: %s",
389 nested_error->message);
390 }
391 }
392 else
393 {
394 SKIP_UNKNOWN (parser,
395 NULL,
396 "Unexpected key in buildopts: %s",
397 (const gchar *)event.data.scalar.value);
398 break;
399 }
400 break;
401
402 default:
403 MMD_YAML_ERROR_EVENT_EXIT (error,
404 event,
405 "Unexpected YAML event in buildopts: %s",
406 mmd_yaml_get_event_name (event.type));
407 break;
408 }
409
410 yaml_event_delete (&event);
411 }
412 return g_steal_pointer (&buildopts);
413 }
414
415 static gboolean
modulemd_buildopts_parse_rpm_buildopts(yaml_parser_t * parser,ModulemdBuildopts * buildopts,gboolean strict,GError ** error)416 modulemd_buildopts_parse_rpm_buildopts (yaml_parser_t *parser,
417 ModulemdBuildopts *buildopts,
418 gboolean strict,
419 GError **error)
420 {
421 MODULEMD_INIT_TRACE ();
422 MMD_INIT_YAML_EVENT (event);
423 gboolean done = FALSE;
424 gboolean in_map = FALSE;
425 g_autofree gchar *value = NULL;
426 g_autoptr (GError) nested_error = NULL;
427
428 /* Read in RPM attributes */
429 while (!done)
430 {
431 YAML_PARSER_PARSE_WITH_EXIT_BOOL (parser, &event, error);
432
433 switch (event.type)
434 {
435 case YAML_MAPPING_START_EVENT: in_map = TRUE; break;
436
437 case YAML_MAPPING_END_EVENT:
438 in_map = FALSE;
439 done = TRUE;
440 break;
441
442 case YAML_SCALAR_EVENT:
443 if (!in_map)
444 {
445 MMD_YAML_ERROR_EVENT_EXIT_BOOL (
446 error, event, "Missing mapping in buildopts rpms entry");
447 }
448
449 if (g_str_equal (event.data.scalar.value, "whitelist"))
450 {
451 g_hash_table_unref (buildopts->whitelist);
452 buildopts->whitelist =
453 modulemd_yaml_parse_string_set (parser, &nested_error);
454 if (buildopts->whitelist == NULL)
455 {
456 MMD_YAML_ERROR_EVENT_EXIT_BOOL (
457 error,
458 event,
459 "Failed to parse whitelist list in buildopts rpms: %s",
460 nested_error->message);
461 }
462 }
463 else if (g_str_equal (event.data.scalar.value, "macros"))
464 {
465 value = modulemd_yaml_parse_string (parser, &nested_error);
466 if (!value)
467 {
468 MMD_YAML_ERROR_EVENT_EXIT_BOOL (
469 error,
470 event,
471 "Failed to parse rpm_macros in buildopts: %s",
472 nested_error->message);
473 }
474 modulemd_buildopts_set_rpm_macros (buildopts, value);
475 g_clear_pointer (&value, g_free);
476 }
477 else
478 {
479 SKIP_UNKNOWN (parser,
480 FALSE,
481 "Unexpected key in buildopts body: %s",
482 (const gchar *)event.data.scalar.value);
483 }
484 break;
485
486 default:
487 MMD_YAML_ERROR_EVENT_EXIT_BOOL (
488 error,
489 event,
490 "Unexpected YAML event in rpm buildopts: %s",
491 mmd_yaml_get_event_name (event.type));
492 break;
493 }
494
495 yaml_event_delete (&event);
496 }
497 return TRUE;
498 }
499
500
501 gboolean
modulemd_buildopts_emit_yaml(ModulemdBuildopts * self,yaml_emitter_t * emitter,GError ** error)502 modulemd_buildopts_emit_yaml (ModulemdBuildopts *self,
503 yaml_emitter_t *emitter,
504 GError **error)
505 {
506 MODULEMD_INIT_TRACE ();
507 int ret;
508 g_auto (GStrv) list = NULL;
509 g_autoptr (GError) nested_error = NULL;
510 MMD_INIT_YAML_EVENT (event);
511
512 ret = mmd_emitter_scalar (
513 emitter, "rpms", YAML_PLAIN_SCALAR_STYLE, &nested_error);
514 if (!ret)
515 {
516 g_propagate_prefixed_error (
517 error,
518 g_steal_pointer (&nested_error),
519 "Failed to emit buildopts 'rpms' constant: ");
520 return FALSE;
521 }
522
523 ret = mmd_emitter_start_mapping (
524 emitter, YAML_BLOCK_MAPPING_STYLE, &nested_error);
525 if (!ret)
526 {
527 g_propagate_prefixed_error (error,
528 g_steal_pointer (&nested_error),
529 "Failed to start buildopts mapping: ");
530 return FALSE;
531 }
532
533 if (modulemd_buildopts_get_rpm_macros (self) != NULL)
534 {
535 ret = mmd_emitter_scalar (
536 emitter, "macros", YAML_PLAIN_SCALAR_STYLE, &nested_error);
537 if (!ret)
538 {
539 g_propagate_prefixed_error (error,
540 g_steal_pointer (&nested_error),
541 "Failed to emit buildopts macros key: ");
542 return FALSE;
543 }
544
545 ret = mmd_emitter_scalar (emitter,
546 modulemd_buildopts_get_rpm_macros (self),
547 YAML_FOLDED_SCALAR_STYLE,
548 &nested_error);
549 if (!ret)
550 {
551 g_propagate_prefixed_error (
552 error,
553 g_steal_pointer (&nested_error),
554 "Failed to emit buildopts macros value: ");
555 return FALSE;
556 }
557 }
558
559 if (g_hash_table_size (self->whitelist) != 0)
560 {
561 ret = mmd_emitter_scalar (
562 emitter, "whitelist", YAML_PLAIN_SCALAR_STYLE, &nested_error);
563 if (!ret)
564 {
565 g_propagate_prefixed_error (
566 error,
567 g_steal_pointer (&nested_error),
568 "Failed to emit buildopts whitelist key: ");
569 return FALSE;
570 }
571
572 list = modulemd_buildopts_get_rpm_whitelist_as_strv (self);
573
574 ret = mmd_emitter_strv (
575 emitter, YAML_BLOCK_SEQUENCE_STYLE, list, &nested_error);
576 if (!ret)
577 {
578 g_propagate_prefixed_error (error,
579 g_steal_pointer (&nested_error),
580 "Failed to emit buildopts whitelist: ");
581 return FALSE;
582 }
583
584 g_clear_pointer (&list, g_strfreev);
585 }
586
587 ret = mmd_emitter_end_mapping (emitter, &nested_error);
588 if (!ret)
589 {
590 g_propagate_prefixed_error (error,
591 g_steal_pointer (&nested_error),
592 "Failed to end buildopts mapping");
593 return FALSE;
594 }
595
596 if (g_hash_table_size (self->arches) != 0)
597 {
598 ret = mmd_emitter_scalar (
599 emitter, "arches", YAML_PLAIN_SCALAR_STYLE, &nested_error);
600 if (!ret)
601 {
602 g_propagate_prefixed_error (error,
603 g_steal_pointer (&nested_error),
604 "Failed to emit buildopts arches key: ");
605 return FALSE;
606 }
607
608 list = modulemd_buildopts_get_arches_as_strv (self);
609
610 ret = mmd_emitter_strv (
611 emitter, YAML_FLOW_SEQUENCE_STYLE, list, &nested_error);
612 if (!ret)
613 {
614 g_propagate_prefixed_error (error,
615 g_steal_pointer (&nested_error),
616 "Failed to emit buildopts arches: ");
617 return FALSE;
618 }
619
620 g_clear_pointer (&list, g_strfreev);
621 }
622
623 return TRUE;
624 }
625