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
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 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 }
216}
217}
218
219
220=item B<pb_conf_cache>
221
222This function caches the configuration file content passed as first parameter into the hash passed in second parameter
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
233# Read the content of the config file and cache it in the %h hash then available for queries
234if ($PBCONFVER < 1) {
235 open(CONF,$cf) || (cluck "Unable to open $cf" && return($lh));
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");
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();
254 warn "unexpected line '$_' in $cf";
255 }
256 }
257 close(CONF);
258
259} else {
260 # Have we already handled that conf file ?
261 next if ((defined $lh) && (defined $lh->{'__cf'}) && (defined $lh->{'__cf'}->{$cf}) && ($lh->{'__cf'}->{$cf} eq 1));
262
263 pb_log(1,"Loading YAML conf file $cf\n");
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 }
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 }
280 # Remember we've seen that conf file
281 $lh->{'__cf'}->{$cf} = 1;
282}
283return($lh);
284}
285
286=item B<pb_conf_add>
287
288This function adds the configuration file to the list last, and cache their content in the %h hash
289
290=cut
291
292sub pb_conf_add {
293
294my $lh;
295
296foreach my $cf (@_) {
297 if (! -r $cf) {
298 pb_log(0,"WARNING: pb_conf_add can not read $cf\n");
299 next;
300 }
301 # Skip already used conf files
302 return($lh) if (defined $pbconffiles{$cf});
303
304 pb_log(2,"DEBUG: pb_conf_add with $cf\n");
305 # The new conf file overload values already managed
306 my $num = keys %pbconffiles;
307 pb_log(2,"DEBUG: pb_conf_cache of $cf at position $num\n");
308 $pbconffiles{$cf} = $num;
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)
315}
316}
317
318
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
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
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;
350my $lh;
351
352$lh = pb_conf_cache($conffile,$lh);
353
354foreach my $param (@param) {
355 push @ptr,$lh->{$param};
356}
357return(@ptr);
358}
359
360=item B<pb_conf_write>
361
362This function writes in the file passed as first parameter the hash of values passed as second parameter
363
364=cut
365
366sub pb_conf_write {
367
368my $conffile = shift;
369my $h = shift;
370
371confess "No configuration file defined to write into !" if (not defined $conffile);
372confess "No hash defined to read from !" if (not defined $h);
373open(CONF,"> $conffile") || (cluck "Unable to write into $conffile" && return);
374
375if ($PBCONFVER < 1) {
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 }
382 }
383} else {
384 pb_log(1,"Writing YAML conf file $conffile\n");
385 delete $h->{'__cf'};
386 print CONF $dpfunc->($h);
387}
388close(CONF);
389}
390
391
392
393=item B<pb_conf_get_in_hash_if>
394
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.
397
398=cut
399
400sub pb_conf_get_in_hash_if {
401
402my $lh = shift || return(());
403my @params = @_;
404my @ptr = ();
405
406pb_log(3,"DEBUG: pb_conf_get_in_hash_if on params ".join(' ',@params)."\n");
407foreach my $k (@params) {
408 push @ptr,$lh->{$k};
409}
410
411pb_log(3,"DEBUG: pb_conf_get_in_hash_if returns\n".Dumper(@ptr));
412return(@ptr);
413}
414
415
416
417=item B<pb_conf_get_if>
418
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.
420
421=cut
422
423sub pb_conf_get_if {
424
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'};
433}
434
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
443=item B<pb_conf_add_last_in_hash>
444
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)
446
447It is used internally by pb_conf_add and is not exported.
448
449=cut
450
451sub pb_conf_add_last_in_hash {
452
453my $ptr = shift;
454
455return if (not defined $ptr);
456# TODO: test $ptr is a hash pointer
457
458# When called without correct initialization, try to work anyway with default as project
459pb_conf_init("default") if (not defined $ENV{'PBPROJ'});
460
461my @params = (sort keys %$ptr);
462
463# Everything is returned via @h
464# @h contains the values overloading what @ptr may contain.
465my @h = pb_conf_get_in_hash_if($h,@params);
466my @ptr = pb_conf_get_in_hash_if($ptr,@params);
467
468my $p1;
469my $p2;
470
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");
474
475foreach my $i (0..$#params) {
476 $p1 = $h[$i];
477 $p2 = $ptr[$i];
478 # Always try to take the param from h in priority
479 # in order to mask what could be defined already in ptr
480 if (not defined $p2) {
481 # exit if no p1 either
482 next if (not defined $p1);
483 } else {
484 # Ref found in p2
485 if (not defined $p1) {
486 # No ref in p1 so use p2's value
487 $p1 = $p2;
488 } else {
489 # Both are defined - handling the overloading
490 # Now copy back into p1 all p2 content
491 # as p1 content always has priority over p2
492 if (not defined $p1->{$ENV{'PBPROJ'}}) {
493 if (defined $p2->{$ENV{'PBPROJ'}}) {
494 $p1->{$ENV{'PBPROJ'}} = $p2->{$ENV{'PBPROJ'}};
495 }
496 }
497 # Now copy back into p1 all p2 content which doesn't exist in p1
498 # # p1 content always has priority over p2
499 foreach my $k (keys %$p2) {
500 $p1->{$k} = $p2->{$k} if (not defined $p1->{$k});
501 }
502 }
503 }
504 $h->{$params[$i]} = $p1;
505}
506pb_log(2,"DEBUG: pb_conf_add_last_in_hash output: ".Dumper($h)."\n");
507}
508
509=item B<pb_conf_get>
510
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);
519my $proj = undef;
520
521if (not defined $ENV{'PBPROJ'}) {
522 $proj = "unknown";
523} else {
524 $proj = $ENV{'PBPROJ'};
525}
526
527confess "No params found for $proj" if (not @return);
528
529foreach my $i (0..$#param) {
530 confess "No $param[$i] defined for $proj" if (not defined $return[$i]);
531}
532return(@return);
533}
534
535
536=item B<pb_conf_get_all>
537
538This function returns an array with all configuration parameters
539
540=cut
541
542sub pb_conf_get_all {
543
544my $ah;
545foreach my $i (sort keys %$h) {
546 $ah->{$i} = $h->{$i} if ($i !~ /^__cf/);
547}
548return(sort keys %$ah);
549}
550
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
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
574open(ORIG,$orig) || (cluck "Unable to open $orig" && return);
575confess "Will not erase existing $dest while transforming $orig" if (-f $dest);
576open(DEST,"> $dest") || (cluck "Unable to write into $dest" && close(ORIG) && return);
577print DEST "---\n";
578my $pbconfverbkp = $PBCONFVER;
579# We force migration from v0 to v1
580$PBCONFVER = 0;
581my $lh0;
582my $lh1;
583$lh0 = pb_conf_cache($orig,$lh0);
584pb_log(2,"lh0:\n".Dumper($lh0)."\n");
585$PBCONFVER = $pbconfverbkp;
586
587pb_log(0,"Converting v0 conf file $orig to v1 conf file $dest\n");
588# We can't just write the YAML if we want to keep comments !
589while (<ORIG>) {
590 if ($_ =~ /^#/) {
591 # Keep comments
592 print DEST $_;
593 } elsif ($_ =~ /^\s*$/) {
594 # Replace empty lines by comments
595 print DEST "#\n";;
596 } else {
597 if (/^\s*([A-z0-9-_]+)\s+(.+)$/) {
598 # Handle parameters
599 my ($param,$void) = ($1, $2);
600 if (not defined $lh1->{$param}) {
601 pb_log(2,"Converting parameter $param\n");
602 my $param2 = $param;
603 # param pburl in v0 is now pbprojurl in v1
604 $param2 =~ s/pburl/pbprojurl/;
605 print DEST "$param2:\n";
606 foreach my $k (keys %{$lh0->{$param}}) {
607 pb_log(2,"Handling key $k\n");
608 if ($lh0->{$param}->{$k} =~ /^\s*$/) {
609 print DEST " $k: !!str \"\"\n";
610 } else {
611 print DEST " $k: $lh0->{$param}->{$k}\n";
612 }
613 }
614 $lh1->{$param} = 1;
615 }
616 } else {
617 pb_log(0,"Unable to convert line $_\n");
618 }
619 }
620}
621close(ORIG);
622close(DEST);
623return();
624}
625
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.