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

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

Avoid returning the cf key for pb_conf_get_all

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