source: ProjectBuilder/devel/pb-modules/lib/ProjectBuilder/Conf.pm@ 2598

Last change on this file since 2598 was 2598, checked in by Bruno Cornec, 4 years ago

Only use ProjectBuilder::YAML when not in setupv

File size: 16.0 KB
Line 
1#!/usr/bin/perl -w
2#
3# ProjectBuilder Conf module
4# Conf files subroutines brought by the the Project-Builder project
5# which can be easily used by wahtever perl project
6#
7# Copyright B. Cornec 2007-today
8# Eric Anderson's changes are (c) Copyright 2012 Hewlett Packard
9# Provided under the GPL v2
10#
11# $Id$
12#
13
14package ProjectBuilder::Conf;
15
16use strict;
17use Carp qw/cluck confess/;
18use Data::Dumper;
19use ProjectBuilder::Base;
20use ProjectBuilder::Version;
21
22# Inherit from the "Exporter" module which handles exporting functions.
23
24use vars qw(@ISA @EXPORT);
25use Exporter;
26
27# Export, by default, all the functions into the namespace of
28# any code which uses this module.
29
30our @ISA = qw(Exporter);
31our @EXPORT = qw(pb_conf_init pb_conf_add pb_conf_read pb_conf_read_if pb_conf_write pb_conf_get pb_conf_get_if pb_conf_get_all pb_conf_get_hash pb_conf_cache pb_conf_update_v0 pb_conf_get_in_hash_if);
32
33# Global hash of conf files
34# Key is the conf file name
35# Value is its rank
36my %pbconffiles;
37
38# Global hash of conf file content
39# Key is the config keyword
40# Value is a hash whose key depends on the nature of the config keyword as documented
41# and value is the confguration value
42# We consider that values can not change during the life of pb
43my $h = ();
44
45my $dpfunc;
46my $ldfunc;
47our ($VERSION,$REVISION,$PBCONFVER) = pb_version_init();
48
49=pod
50
51=head1 NAME
52
53ProjectBuilder::Conf, part of the project-builder.org - module dealing with configuration files
54
55=head1 DESCRIPTION
56
57This modules provides functions dealing with configuration files.
58
59=head1 SYNOPSIS
60
61 use ProjectBuilder::Conf;
62
63 #
64 # Read hash codes of values from a configuration file and return table of pointers
65 #
66 my ($k1, $k2) = pb_conf_read_if("$ENV{'HOME'}/.pbrc.yml","key1","key2");
67 my ($k) = pb_conf_read("$ENV{'HOME'}/.pbrc.yml","key");
68
69=head1 USAGE
70
71The configuration files are loaded in a specific order from most generic to the most specific
72to allow for overwrite to work:
73
74For recent versions of pb (>= 0.15):
751. /usr/share/pb/pb.yml - the read-only system conf file provided by install
762. /etc/pb/pb.yml - the same global conf file given to the sysadmin in order to make system wide modifications
773. /path/to/project.yml - Configuration file for the project we're building for
784. /vm|vepath/to/.pbrc.yml - configuration file for VM, VE or RM specific parameters. Cumulative should be orthogonal
795. $HOME/.pbrc.yml - user's configuration file
80
81For versions of pb up to 0.14:
821. /usr/share/pb/pb.conf - the read-only system conf file provided by install
832. /etc/pb/pb.conf - the same global conf file given to the sysadmin in order to make system wide modifications
843. /path/to/project.pb - Configuration file for the project we're building for
854. /(vm|ve|rm)path/to/.pbrc - configuration file for VM, VE or RM specific parameters. Cumulative should be orthogonal
865. $HOME/.pbrc - user's configuration file
87
88The format of the configuration file is as follows:
89
90For recent versions of pb (>= 0.15):
91YAML format is now used - The version of the configuration files is
92
93Supposing the file is called "$ENV{'HOME'}/.pbrc.yml", containing the following:
94
95 $ cat $HOME/.pbrc.yml
96 ---
97 pbver:
98 - pb: 3
99 - default: 1
100 pblist:
101 - pb: 12,25
102
103calling it like this:
104
105 my ($k1, $k2) = pb_conf_read_if("$ENV{'HOME'}/.pbrc.yml","pbver","pblist");
106
107will allow to get the mapping:
108
109 $k1->{'pb'} contains 3
110 $k1->{'default'} contains 1
111 $k2->{'pb'} contains 12,25
112
113For versions of pb up to 0.14:
114An own format was used - The version of the configuration files is 0
115
116key tag = value1,value2,...
117
118Supposing the file is called "$ENV{'HOME'}/.pbrc", containing the following:
119
120 $ cat $HOME/.pbrc
121 pbver pb = 3
122 pbver default = 1
123 pblist pb = 12,25
124
125calling it like this:
126
127 my ($k1, $k2) = pb_conf_read_if("$ENV{'HOME'}/.pbrc","pbver","pblist");
128
129will allow to get the mapping:
130
131 $k1->{'pb'} contains 3
132 $k1->{'default'} contains 1
133 $k2->{'pb'} contains 12,25
134
135Valid chars for keys and tags are letters, numbers, '-' and '_'.
136
137=over 4
138
139=item B<pb_conf_init>
140
141This function setup the environment PBPROJ for project-builder function usage from other projects.
142The first parameter is the project name.
143It sets up environment variables (PBPROJ)
144
145=cut
146
147sub pb_conf_init {
148
149my $proj=shift;
150
151pb_log(2,"Entering pb_conf_init\n");
152#
153# Check project name
154# Could be with env var PBPROJ
155# or option -p
156# if not defined take the first in conf file
157#
158if ((defined $ENV{'PBPROJ'}) &&
159 (not defined $proj)) {
160 pb_log(2,"PBPROJ env var setup ($ENV{'PBPROJ'}) so using it\n");
161 $proj = $ENV{'PBPROJ'};
162}
163
164if (defined $proj) {
165 $ENV{'PBPROJ'} = $proj;
166} else {
167 $ENV{'PBPROJ'} = "default";
168}
169pb_log(1,"PBPROJ = $ENV{'PBPROJ'}\n");
170
171# Manage YAML modules to use
172
173if ($PBCONFVER >= 1) {
174 eval {
175 require YAML::XS;
176 YAML::XS->import();
177 };
178 if ($@) {
179 eval {
180 require YAML;
181 YAML->import();
182 };
183 if ($@) {
184 eval {
185 # No YAML found using a more std but less complete one. Old perl only
186 require Module::Build::YAML;
187 Module::Build::YAML->import();
188 };
189 if ($@) {
190 eval {
191 # No YAML found using a more std but less complete one. Old perl only
192 require YAML::Tiny;
193 YAML::Tiny->import();
194 };
195 if ($@) {
196 # Here we use an embedded YAML code for distro lacking any YAML module
197 if (not defined ($ENV{'PBSETUPV'})) {
198 # Don't do that for setupv only for other scripts
199 # as the module is already embedded and would conflict
200 require ProjectBuilder::YAML;
201 ProjectBuilder::YAML->import();
202 };
203 $dpfunc = \&pb_Dump;
204 $ldfunc = \&pb_LoadFile;
205 } else {
206 $dpfunc = \&YAML::Tiny::Dump;
207 $ldfunc = \&YAML::Tiny::LoadFile;
208 }
209 } else {
210 $dpfunc = \&Module::Build::YAML::Dump;
211 $ldfunc = \&Module::Build::YAML::LoadFile;
212 }
213 } else {
214 $dpfunc = \&YAML::Dump;
215 $ldfunc = \&YAML::LoadFile;
216 }
217 } else {
218 $dpfunc = \&YAML::XS::Dump;
219 $ldfunc = \&YAML::XS::LoadFile;
220 }
221}
222}
223
224
225=item B<pb_conf_cache>
226
227This function caches the configuration file content passed as first parameter into the hash passed in second parameter
228It returns the modified hash
229Can be used in correlation with the %h hash to store permanently values or not if temporarily.
230
231=cut
232
233sub pb_conf_cache {
234
235my $cf = shift;
236my $lh = shift;
237
238# Read the content of the config file and cache it in the %h hash then available for queries
239if ($PBCONFVER < 1) {
240 open(CONF,$cf) || (cluck "Unable to open $cf" && return($lh));
241 # This is the original conf file format for versions up to 0.14
242 while(<CONF>) {
243 next if (/^#/);
244 if (/^\s*([A-z0-9-_.]+)\s+([[A-z0-9-_.\?\[\]\*\+\\]+)\s*=\s*(.*)$/) {
245 pb_log(3,"DEBUG: 1:$1 2:$2 3:$3\n");
246 my ($what, $var, $value) = ($1, $2, $3);
247 # Add support for multi-lines
248 while ($value =~ s/\\\s*$//o) {
249 $_ = <CONF>;
250 die "Still processing continuations for $what $var at EOF" if (not defined $_);
251 s/[\r\n]//go;
252 $value .= "\n$_";
253 }
254 $lh->{$what}->{$var}=$value;
255 } elsif ((/^\s*#/o) || (/^\s*$/o)) {
256 # ignore
257 } else {
258 chomp();
259 warn "unexpected line '$_' in $cf";
260 }
261 }
262 close(CONF);
263
264} else {
265 # Have we already handled that conf file ?
266 next if ((defined $lh) && (defined $lh->{'__cf'}) && (defined $lh->{'__cf'}->{$cf}) && ($lh->{'__cf'}->{$cf} eq 1));
267
268 pb_log(1,"Loading YAML conf file $cf\n");
269 my $lh0;
270 eval { $lh0 = $ldfunc->($cf); };
271 if ($@) {
272 # Repeat to get the YAML error line
273 $lh0 = $ldfunc->($cf);
274 die "Unable to analyze YAML conf file $cf\n";
275 }
276 foreach my $k (keys %$lh0) {
277 if (defined $lh->{$k}) {
278 foreach my $k2 (keys %{$lh0->{$k}}) {
279 $lh->{$k}->{$k2} = $lh0->{$k}->{$k2};
280 }
281 } else {
282 $lh->{$k} = $lh0->{$k};
283 }
284 }
285 # Remember we've seen that conf file
286 $lh->{'__cf'}->{$cf} = 1;
287}
288return($lh);
289}
290
291=item B<pb_conf_add>
292
293This function adds the configuration file to the list last, and cache their content in the %h hash
294
295=cut
296
297sub pb_conf_add {
298
299my $lh;
300
301foreach my $cf (@_) {
302 if (! -r $cf) {
303 pb_log(0,"WARNING: pb_conf_add can not read $cf\n");
304 next;
305 }
306 # Skip already used conf files
307 return($lh) if (defined $pbconffiles{$cf});
308
309 pb_log(2,"DEBUG: pb_conf_add with $cf\n");
310 # The new conf file overload values already managed
311 my $num = keys %pbconffiles;
312 pb_log(2,"DEBUG: pb_conf_cache of $cf at position $num\n");
313 $pbconffiles{$cf} = $num;
314
315 # Read the content of the config file
316 $lh = pb_conf_cache($cf,$lh);
317 # and cache it in the %h hash for further queries but after the previous
318 # as we load conf files in reverse order (most precise first)
319 pb_conf_add_last_in_hash($lh)
320}
321}
322
323
324=item B<pb_conf_read_if>
325
326This function returns a table of pointers on hashes
327corresponding to the keys in a configuration file passed in parameter.
328If that file doesn't exist, it returns undef.
329
330The file read is forgotten after its usage. If you want permanent caching of the data, use pb_conf_add then pb_conf_get
331
332=cut
333
334sub pb_conf_read_if {
335
336my $conffile = shift;
337my @param = @_;
338
339open(CONF,$conffile) || return((undef));
340close(CONF);
341return(pb_conf_read($conffile,@param));
342}
343
344=item B<pb_conf_read>
345
346This function is similar to B<pb_conf_read_if> except that it dies when the file in parameter doesn't exist.
347
348=cut
349
350sub pb_conf_read {
351
352my $conffile = shift;
353my @param = @_;
354my @ptr;
355my $lh;
356
357$lh = pb_conf_cache($conffile,$lh);
358
359foreach my $param (@param) {
360 push @ptr,$lh->{$param};
361}
362return(@ptr);
363}
364
365=item B<pb_conf_write>
366
367This function writes in the file passed as first parameter the hash of values passed as second parameter
368
369=cut
370
371sub pb_conf_write {
372
373my $conffile = shift;
374my $h = shift;
375
376confess "No configuration file defined to write into !" if (not defined $conffile);
377confess "No hash defined to read from !" if (not defined $h);
378open(CONF,"> $conffile") || (cluck "Unable to write into $conffile" && return);
379
380if ($PBCONFVER < 1) {
381 # This is the original conf file format for versions up to 0.14
382 foreach my $p (sort keys %$h) {
383 my $j = $h->{$p};
384 foreach my $k (sort keys %$j) {
385 print CONF "$p $k = $j->{$k}\n";
386 }
387 }
388} else {
389 pb_log(1,"Writing YAML conf file $conffile\n");
390 delete $h->{'__cf'};
391 print CONF $dpfunc->($h);
392}
393close(CONF);
394}
395
396
397
398=item B<pb_conf_get_in_hash_if>
399
400This function returns a table, corresponding to a set of values queried in the hash passed in parameter or undef if it doesn't exist.
401It takes a table of keys as an input parameter.
402
403=cut
404
405sub pb_conf_get_in_hash_if {
406
407my $lh = shift || return(());
408my @params = @_;
409my @ptr = ();
410
411pb_log(3,"DEBUG: pb_conf_get_in_hash_if on params ".join(' ',@params)."\n");
412foreach my $k (@params) {
413 push @ptr,$lh->{$k};
414}
415
416pb_log(3,"DEBUG: pb_conf_get_in_hash_if returns\n".Dumper(@ptr));
417return(@ptr);
418}
419
420
421
422=item B<pb_conf_get_if>
423
424This function returns a table, corresponding to a set of values queried in the %h hash or undef if it doen't exist. It takes a table of keys as an input parameter.
425
426=cut
427
428sub pb_conf_get_if {
429
430my @param = @_;
431my @return = pb_conf_get_in_hash_if($h,@_);
432my $proj = undef;
433
434if (not defined $ENV{'PBPROJ'}) {
435 $proj = "unknown";
436} else {
437 $proj = $ENV{'PBPROJ'};
438}
439
440foreach my $i (0..$#param) {
441 if (not defined $return[$i]->{$proj}) {
442 $return[$i]->{$proj} = $return[$i]->{'default'} if (defined $return[$i]->{'default'});
443 }
444}
445return(@return);
446}
447
448=item B<pb_conf_add_last_in_hash>
449
450This function merges the values passed in the hash parameter into the %h hash, but only if itdoesn't already contain a value, or if the value is more precise (real value instead of default)
451
452It is used internally by pb_conf_add and is not exported.
453
454=cut
455
456sub pb_conf_add_last_in_hash {
457
458my $ptr = shift;
459
460return if (not defined $ptr);
461# TODO: test $ptr is a hash pointer
462
463# When called without correct initialization, try to work anyway with default as project
464pb_conf_init("default") if (not defined $ENV{'PBPROJ'});
465
466my @params = (sort keys %$ptr);
467
468# Everything is returned via @h
469# @h contains the values overloading what @ptr may contain.
470my @h = pb_conf_get_in_hash_if($h,@params);
471my @ptr = pb_conf_get_in_hash_if($ptr,@params);
472
473my $p1;
474my $p2;
475
476pb_log(3,"DEBUG: pb_conf_add_last_in_hash params: ".Dumper(@params)."\n");
477pb_log(3,"DEBUG: pb_conf_add_last_in_hash current hash: ".Dumper(@h)."\n");
478pb_log(3,"DEBUG: pb_conf_add_last_in_hash new inputs: ".Dumper(@ptr)."\n");
479
480foreach my $i (0..$#params) {
481 $p1 = $h[$i];
482 $p2 = $ptr[$i];
483 # Always try to take the param from h in priority
484 # in order to mask what could be defined already in ptr
485 if (not defined $p2) {
486 # exit if no p1 either
487 next if (not defined $p1);
488 } else {
489 # Ref found in p2
490 if (not defined $p1) {
491 # No ref in p1 so use p2's value
492 $p1 = $p2;
493 } else {
494 # Both are defined - handling the overloading
495 # Now copy back into p1 all p2 content
496 # as p1 content always has priority over p2
497 if (not defined $p1->{$ENV{'PBPROJ'}}) {
498 if (defined $p2->{$ENV{'PBPROJ'}}) {
499 $p1->{$ENV{'PBPROJ'}} = $p2->{$ENV{'PBPROJ'}};
500 }
501 }
502 # Now copy back into p1 all p2 content which doesn't exist in p1
503 # # p1 content always has priority over p2
504 foreach my $k (keys %$p2) {
505 $p1->{$k} = $p2->{$k} if (not defined $p1->{$k});
506 }
507 }
508 }
509 $h->{$params[$i]} = $p1;
510}
511pb_log(2,"DEBUG: pb_conf_add_last_in_hash output: ".Dumper($h)."\n");
512}
513
514=item B<pb_conf_get>
515
516This function is the same B<pb_conf_get_if>, except that it tests each returned value as they need to exist in that case.
517
518=cut
519
520sub pb_conf_get {
521
522my @param = @_;
523my @return = pb_conf_get_if(@param);
524my $proj = undef;
525
526if (not defined $ENV{'PBPROJ'}) {
527 $proj = "unknown";
528} else {
529 $proj = $ENV{'PBPROJ'};
530}
531
532confess "No params found for $proj" if (not @return);
533
534foreach my $i (0..$#param) {
535 confess "No $param[$i] defined for $proj" if (not defined $return[$i]);
536}
537return(@return);
538}
539
540
541=item B<pb_conf_get_all>
542
543This function returns an array with all configuration parameters
544
545=cut
546
547sub pb_conf_get_all {
548
549my $ah;
550foreach my $i (sort keys %$h) {
551 $ah->{$i} = $h->{$i} if ($i !~ /^__cf/);
552}
553return(sort keys %$ah);
554}
555
556
557=item B<pb_conf_get_hash>
558
559This function returns a pointer to the hash with all configuration parameters
560
561=cut
562
563sub pb_conf_get_hash {
564
565return($h);
566}
567
568=item B<pb_conf_update_v0>
569
570This function transform the old configuration v0 file as first param into a new v1 one as second param
571
572=cut
573
574sub pb_conf_update_v0 {
575
576my $orig = shift;
577my $dest = shift;
578
579open(ORIG,$orig) || (cluck "Unable to open $orig" && return);
580confess "Will not erase existing $dest while transforming $orig" if (-f $dest);
581open(DEST,"> $dest") || (cluck "Unable to write into $dest" && close(ORIG) && return);
582print DEST "---\n";
583my $pbconfverbkp = $PBCONFVER;
584# We force migration from v0 to v1
585$PBCONFVER = 0;
586my $lh0;
587my $lh1;
588$lh0 = pb_conf_cache($orig,$lh0);
589pb_log(2,"lh0:\n".Dumper($lh0)."\n");
590$PBCONFVER = $pbconfverbkp;
591
592pb_log(0,"Converting v0 conf file $orig to v1 conf file $dest\n");
593# We can't just write the YAML if we want to keep comments !
594while (<ORIG>) {
595 if ($_ =~ /^#/) {
596 # Keep comments
597 print DEST $_;
598 } elsif ($_ =~ /^\s*$/) {
599 # Replace empty lines by comments
600 print DEST "#\n";;
601 } else {
602 if (/^\s*([A-z0-9-_]+)\s+(.+)$/) {
603 # Handle parameters
604 my ($param,$void) = ($1, $2);
605 if (not defined $lh1->{$param}) {
606 pb_log(2,"Converting parameter $param\n");
607 my $param2 = $param;
608 # param pburl in v0 is now pbprojurl in v1
609 $param2 =~ s/pburl/pbprojurl/;
610 print DEST "$param2:\n";
611 foreach my $k (keys %{$lh0->{$param}}) {
612 pb_log(2,"Handling key $k\n");
613 if ($lh0->{$param}->{$k} =~ /^\s*$/) {
614 print DEST " $k: !!str \"\"\n";
615 } else {
616 print DEST " $k: $lh0->{$param}->{$k}\n";
617 }
618 }
619 $lh1->{$param} = 1;
620 }
621 } else {
622 pb_log(0,"Unable to convert line $_\n");
623 }
624 }
625}
626close(ORIG);
627close(DEST);
628return();
629}
630
631=back
632
633=head1 WEB SITES
634
635The main Web site of the project is available at L<http://www.project-builder.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.project-builder.org/>.
636
637=head1 USER MAILING LIST
638
639None exists for the moment.
640
641=head1 AUTHORS
642
643The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
644
645=head1 COPYRIGHT
646
647Project-Builder.org is distributed under the GPL v2.0 license
648described in the file C<COPYING> included with the distribution.
649
650=cut
651
652
6531;
Note: See TracBrowser for help on using the repository browser.