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

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

Various perf improvements

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