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

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

Avoid usage of Module::Build::YAML as creating issues on old distros and prefer Tiny:YAML embedded

File size: 15.7 KB
RevLine 
[405]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#
[2488]7# Copyright B. Cornec 2007-today
[1528]8# Eric Anderson's changes are (c) Copyright 2012 Hewlett Packard
9# Provided under the GPL v2
10#
[405]11# $Id$
12#
13
14package ProjectBuilder::Conf;
15
16use strict;
[2362]17use Carp qw/cluck confess/;
[405]18use Data::Dumper;
19use ProjectBuilder::Base;
[1148]20use ProjectBuilder::Version;
[405]21
22# Inherit from the "Exporter" module which handles exporting functions.
23
[2500]24use vars qw(@ISA @EXPORT);
[405]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);
[2426]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);
[405]32
[898]33# Global hash of conf files
34# Key is the conf file name
35# Value is its rank
36my %pbconffiles;
[409]37
[1495]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
[898]42# We consider that values can not change during the life of pb
[1495]43my $h = ();
[898]44
[2500]45my $dpfunc;
46my $ldfunc;
47our ($VERSION,$REVISION,$PBCONFVER) = pb_version_init();
48
[405]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 #
[2252]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");
[405]68
69=head1 USAGE
70
[2152]71The configuration files are loaded in a specific order from most generic to the most specific
72to allow for overwrite to work:
73
[2250]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:
[2241]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
[2152]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
[2250]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
[2257]96 ---
[2250]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
[405]137=over 4
138
[505]139=item B<pb_conf_init>
140
[898]141This function setup the environment PBPROJ for project-builder function usage from other projects.
[505]142The first parameter is the project name.
[898]143It sets up environment variables (PBPROJ)
[505]144
145=cut
146
147sub pb_conf_init {
148
[1907]149my $proj=shift;
[505]150
[2500]151pb_log(2,"Entering pb_conf_init\n");
[1584]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
[505]164if (defined $proj) {
165 $ENV{'PBPROJ'} = $proj;
166} else {
167 $ENV{'PBPROJ'} = "default";
168}
[1495]169pb_log(1,"PBPROJ = $ENV{'PBPROJ'}\n");
[2500]170
171# Manage YAML modules to use
172
173if ($PBCONFVER >= 1) {
174 eval {
175 require YAML::XS;
176 YAML::XS->import();
177 };
[2598]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
[2604]186 require YAML::Tiny;
187 YAML::Tiny->import();
[2598]188 };
189 if ($@) {
[2604]190 # Here we use an embedded YAML code for distro lacking any YAML module
191 if (not defined ($ENV{'PBSETUPV'})) {
192 # Don't do that for setupv only for other scripts
193 # as the module is already embedded and would conflict
194 require ProjectBuilder::YAML;
195 ProjectBuilder::YAML->import();
[2598]196 };
[2604]197 $dpfunc = \&pb_Dump;
198 $ldfunc = \&pb_LoadFile;
[2598]199 } else {
[2604]200 $dpfunc = \&YAML::Tiny::Dump;
201 $ldfunc = \&YAML::Tiny::LoadFile;
[2598]202 }
203 } else {
204 $dpfunc = \&YAML::Dump;
[2500]205 $ldfunc = \&YAML::LoadFile;
[2598]206 }
207 } else {
208 $dpfunc = \&YAML::XS::Dump;
[2500]209 $ldfunc = \&YAML::XS::LoadFile;
[2598]210 }
[505]211}
[2500]212}
[505]213
214
[1495]215=item B<pb_conf_cache>
[505]216
[2250]217This function caches the configuration file content passed as first parameter into the hash passed in second parameter
[1495]218It returns the modified hash
219Can be used in correlation with the %h hash to store permanently values or not if temporarily.
220
221=cut
222
223sub pb_conf_cache {
224
225my $cf = shift;
226my $lh = shift;
227
[2077]228# Read the content of the config file and cache it in the %h hash then available for queries
[2279]229if ($PBCONFVER < 1) {
[2434]230 open(CONF,$cf) || (cluck "Unable to open $cf" && return($lh));
[2176]231 # This is the original conf file format for versions up to 0.14
232 while(<CONF>) {
233 next if (/^#/);
234 if (/^\s*([A-z0-9-_.]+)\s+([[A-z0-9-_.\?\[\]\*\+\\]+)\s*=\s*(.*)$/) {
235 pb_log(3,"DEBUG: 1:$1 2:$2 3:$3\n");
[2253]236 my ($what, $var, $value) = ($1, $2, $3);
237 # Add support for multi-lines
238 while ($value =~ s/\\\s*$//o) {
239 $_ = <CONF>;
240 die "Still processing continuations for $what $var at EOF" if (not defined $_);
241 s/[\r\n]//go;
242 $value .= "\n$_";
243 }
244 $lh->{$what}->{$var}=$value;
245 } elsif ((/^\s*#/o) || (/^\s*$/o)) {
246 # ignore
247 } else {
248 chomp();
[2256]249 warn "unexpected line '$_' in $cf";
[2176]250 }
[1495]251 }
[2176]252 close(CONF);
[2500]253
[2176]254} else {
[2491]255 # Have we already handled that conf file ?
[2494]256 next if ((defined $lh) && (defined $lh->{'__cf'}) && (defined $lh->{'__cf'}->{$cf}) && ($lh->{'__cf'}->{$cf} eq 1));
[2491]257
[2261]258 pb_log(1,"Loading YAML conf file $cf\n");
[2402]259 my $lh0;
260 eval { $lh0 = $ldfunc->($cf); };
261 if ($@) {
262 # Repeat to get the YAML error line
263 $lh0 = $ldfunc->($cf);
264 die "Unable to analyze YAML conf file $cf\n";
265 }
[2263]266 foreach my $k (keys %$lh0) {
267 if (defined $lh->{$k}) {
268 foreach my $k2 (keys %{$lh0->{$k}}) {
269 $lh->{$k}->{$k2} = $lh0->{$k}->{$k2};
270 }
271 } else {
272 $lh->{$k} = $lh0->{$k};
273 }
274 }
[2491]275 # Remember we've seen that conf file
276 $lh->{'__cf'}->{$cf} = 1;
[1495]277}
278return($lh);
279}
280
[409]281=item B<pb_conf_add>
282
[1495]283This function adds the configuration file to the list last, and cache their content in the %h hash
[409]284
285=cut
286
287sub pb_conf_add {
288
[1495]289my $lh;
[898]290
291foreach my $cf (@_) {
[1495]292 if (! -r $cf) {
293 pb_log(0,"WARNING: pb_conf_add can not read $cf\n");
294 next;
295 }
[898]296 # Skip already used conf files
[1495]297 return($lh) if (defined $pbconffiles{$cf});
298
[2491]299 pb_log(2,"DEBUG: pb_conf_add with $cf\n");
[2154]300 # The new conf file overload values already managed
[898]301 my $num = keys %pbconffiles;
[1495]302 pb_log(2,"DEBUG: pb_conf_cache of $cf at position $num\n");
[898]303 $pbconffiles{$cf} = $num;
[1495]304
305 # Read the content of the config file
306 $lh = pb_conf_cache($cf,$lh);
307 # and cache it in the %h hash for further queries but after the previous
308 # as we load conf files in reverse order (most precise first)
309 pb_conf_add_last_in_hash($lh)
[409]310}
[898]311}
[409]312
[1495]313
[405]314=item B<pb_conf_read_if>
315
316This function returns a table of pointers on hashes
317corresponding to the keys in a configuration file passed in parameter.
318If that file doesn't exist, it returns undef.
319
[1495]320The file read is forgotten after its usage. If you want permanent caching of the data, use pb_conf_add then pb_conf_get
321
[405]322=cut
323
324sub pb_conf_read_if {
325
326my $conffile = shift;
327my @param = @_;
328
329open(CONF,$conffile) || return((undef));
330close(CONF);
331return(pb_conf_read($conffile,@param));
332}
333
334=item B<pb_conf_read>
335
336This function is similar to B<pb_conf_read_if> except that it dies when the file in parameter doesn't exist.
337
338=cut
339
340sub pb_conf_read {
341
342my $conffile = shift;
343my @param = @_;
344my @ptr;
[1495]345my $lh;
[405]346
[1495]347$lh = pb_conf_cache($conffile,$lh);
348
349foreach my $param (@param) {
350 push @ptr,$lh->{$param};
[405]351}
[1495]352return(@ptr);
353}
[405]354
[1904]355=item B<pb_conf_write>
[1495]356
[2278]357This function writes in the file passed as first parameter the hash of values passed as second parameter
[1495]358
[1904]359=cut
360
361sub pb_conf_write {
362
363my $conffile = shift;
[1905]364my $h = shift;
[1904]365
[1905]366confess "No configuration file defined to write into !" if (not defined $conffile);
367confess "No hash defined to read from !" if (not defined $h);
[2434]368open(CONF,"> $conffile") || (cluck "Unable to write into $conffile" && return);
[1904]369
[2279]370if ($PBCONFVER < 1) {
[2176]371 # This is the original conf file format for versions up to 0.14
372 foreach my $p (sort keys %$h) {
373 my $j = $h->{$p};
374 foreach my $k (sort keys %$j) {
375 print CONF "$p $k = $j->{$k}\n";
376 }
[1904]377 }
[2176]378} else {
[2261]379 pb_log(1,"Writing YAML conf file $conffile\n");
[2494]380 delete $h->{'__cf'};
[2249]381 print CONF $dpfunc->($h);
[1904]382}
383close(CONF);
384}
385
386
387
[1495]388=item B<pb_conf_get_in_hash_if>
389
[1594]390This function returns a table, corresponding to a set of values queried in the hash passed in parameter or undef if it doesn't exist.
391It takes a table of keys as an input parameter.
[1495]392
393=cut
394
395sub pb_conf_get_in_hash_if {
396
397my $lh = shift || return(());
398my @params = @_;
399my @ptr = ();
400
[2488]401pb_log(3,"DEBUG: pb_conf_get_in_hash_if on params ".join(' ',@params)."\n");
[1495]402foreach my $k (@params) {
403 push @ptr,$lh->{$k};
[405]404}
[1495]405
[2488]406pb_log(3,"DEBUG: pb_conf_get_in_hash_if returns\n".Dumper(@ptr));
[405]407return(@ptr);
408}
409
[1495]410
411
[409]412=item B<pb_conf_get_if>
[405]413
[1495]414This 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.
[405]415
[409]416=cut
417
418sub pb_conf_get_if {
419
[2154]420my @param = @_;
421my @return = pb_conf_get_in_hash_if($h,@_);
422my $proj = undef;
423
424if (not defined $ENV{'PBPROJ'}) {
425 $proj = "unknown";
426} else {
427 $proj = $ENV{'PBPROJ'};
[405]428}
[409]429
[2154]430foreach my $i (0..$#param) {
431 if (not defined $return[$i]->{$proj}) {
432 $return[$i]->{$proj} = $return[$i]->{'default'} if (defined $return[$i]->{'default'});
433 }
434}
435return(@return);
436}
437
[1495]438=item B<pb_conf_add_last_in_hash>
[405]439
[1495]440This 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)
[405]441
[1495]442It is used internally by pb_conf_add and is not exported.
[409]443
444=cut
445
[1495]446sub pb_conf_add_last_in_hash {
[409]447
[1907]448my $ptr = shift;
[409]449
[1495]450return if (not defined $ptr);
451# TODO: test $ptr is a hash pointer
[405]452
[1509]453# When called without correct initialization, try to work anyway with default as project
454pb_conf_init("default") if (not defined $ENV{'PBPROJ'});
455
[1495]456my @params = (sort keys %$ptr);
[405]457
[1495]458# Everything is returned via @h
459# @h contains the values overloading what @ptr may contain.
[2154]460my @h = pb_conf_get_in_hash_if($h,@params);
[1495]461my @ptr = pb_conf_get_in_hash_if($ptr,@params);
[409]462
[405]463my $p1;
464my $p2;
465
[2488]466pb_log(3,"DEBUG: pb_conf_add_last_in_hash params: ".Dumper(@params)."\n");
467pb_log(3,"DEBUG: pb_conf_add_last_in_hash current hash: ".Dumper(@h)."\n");
468pb_log(3,"DEBUG: pb_conf_add_last_in_hash new inputs: ".Dumper(@ptr)."\n");
[405]469
[1495]470foreach my $i (0..$#params) {
471 $p1 = $h[$i];
472 $p2 = $ptr[$i];
[2154]473 # Always try to take the param from h in priority
[1495]474 # in order to mask what could be defined already in ptr
[405]475 if (not defined $p2) {
[415]476 # exit if no p1 either
[1509]477 next if (not defined $p1);
[405]478 } else {
[409]479 # Ref found in p2
[405]480 if (not defined $p1) {
[409]481 # No ref in p1 so use p2's value
[405]482 $p1 = $p2;
483 } else {
484 # Both are defined - handling the overloading
[2154]485 # Now copy back into p1 all p2 content
486 # as p1 content always has priority over p2
[405]487 if (not defined $p1->{$ENV{'PBPROJ'}}) {
488 if (defined $p2->{$ENV{'PBPROJ'}}) {
[1594]489 $p1->{$ENV{'PBPROJ'}} = $p2->{$ENV{'PBPROJ'}};
[405]490 }
491 }
492 # Now copy back into p1 all p2 content which doesn't exist in p1
[2154]493 # # p1 content always has priority over p2
[405]494 foreach my $k (keys %$p2) {
495 $p1->{$k} = $p2->{$k} if (not defined $p1->{$k});
496 }
497 }
498 }
[1495]499 $h->{$params[$i]} = $p1;
[405]500}
[1495]501pb_log(2,"DEBUG: pb_conf_add_last_in_hash output: ".Dumper($h)."\n");
[405]502}
503
[409]504=item B<pb_conf_get>
[405]505
[409]506This function is the same B<pb_conf_get_if>, except that it tests each returned value as they need to exist in that case.
507
508=cut
509
510sub pb_conf_get {
511
512my @param = @_;
513my @return = pb_conf_get_if(@param);
[932]514my $proj = undef;
[409]515
[932]516if (not defined $ENV{'PBPROJ'}) {
517 $proj = "unknown";
518} else {
519 $proj = $ENV{'PBPROJ'};
520}
[409]521
[1538]522confess "No params found for $proj" if (not @return);
[932]523
[409]524foreach my $i (0..$#param) {
[1507]525 confess "No $param[$i] defined for $proj" if (not defined $return[$i]);
[409]526}
527return(@return);
528}
529
[1495]530
[1694]531=item B<pb_conf_get_all>
532
[2077]533This function returns an array with all configuration parameters
[1694]534
535=cut
536
537sub pb_conf_get_all {
538
[2510]539my $ah;
540foreach my $i (sort keys %$h) {
541 $ah->{$i} = $h->{$i} if ($i !~ /^__cf/);
[1694]542}
[2510]543return(sort keys %$ah);
544}
[1694]545
[2077]546
547=item B<pb_conf_get_hash>
548
549This function returns a pointer to the hash with all configuration parameters
550
551=cut
552
553sub pb_conf_get_hash {
554
555return($h);
556}
557
[2253]558=item B<pb_conf_update_v0>
559
560This function transform the old configuration v0 file as first param into a new v1 one as second param
561
562=cut
563
564sub pb_conf_update_v0 {
565
566my $orig = shift;
567my $dest = shift;
568
[2434]569open(ORIG,$orig) || (cluck "Unable to open $orig" && return);
[2253]570confess "Will not erase existing $dest while transforming $orig" if (-f $dest);
[2434]571open(DEST,"> $dest") || (cluck "Unable to write into $dest" && close(ORIG) && return);
[2257]572print DEST "---\n";
[2253]573my $pbconfverbkp = $PBCONFVER;
574# We force migration from v0 to v1
575$PBCONFVER = 0;
576my $lh0;
[2257]577my $lh1;
[2253]578$lh0 = pb_conf_cache($orig,$lh0);
[2263]579pb_log(2,"lh0:\n".Dumper($lh0)."\n");
[2253]580$PBCONFVER = $pbconfverbkp;
581
[2264]582pb_log(0,"Converting v0 conf file $orig to v1 conf file $dest\n");
[2402]583# We can't just write the YAML if we want to keep comments !
[2253]584while (<ORIG>) {
585 if ($_ =~ /^#/) {
586 # Keep comments
587 print DEST $_;
[2257]588 } elsif ($_ =~ /^\s*$/) {
589 # Replace empty lines by comments
590 print DEST "#\n";;
[2253]591 } else {
592 if (/^\s*([A-z0-9-_]+)\s+(.+)$/) {
593 # Handle parameters
[2257]594 my ($param,$void) = ($1, $2);
595 if (not defined $lh1->{$param}) {
596 pb_log(2,"Converting parameter $param\n");
[2300]597 my $param2 = $param;
598 # param pburl in v0 is now pbprojurl in v1
599 $param2 =~ s/pburl/pbprojurl/;
600 print DEST "$param2:\n";
[2257]601 foreach my $k (keys %{$lh0->{$param}}) {
602 pb_log(2,"Handling key $k\n");
[2264]603 if ($lh0->{$param}->{$k} =~ /^\s*$/) {
604 print DEST " $k: !!str \"\"\n";
605 } else {
606 print DEST " $k: $lh0->{$param}->{$k}\n";
607 }
[2257]608 }
609 $lh1->{$param} = 1;
610 }
[2253]611 } else {
[2257]612 pb_log(0,"Unable to convert line $_\n");
[2253]613 }
614 }
615}
616close(ORIG);
617close(DEST);
618return();
619}
620
[405]621=back
622
623=head1 WEB SITES
624
625The 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/>.
626
627=head1 USER MAILING LIST
628
629None exists for the moment.
630
631=head1 AUTHORS
632
633The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
634
635=head1 COPYRIGHT
636
637Project-Builder.org is distributed under the GPL v2.0 license
638described in the file C<COPYING> included with the distribution.
639
640=cut
641
642
6431;
Note: See TracBrowser for help on using the repository browser.