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

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

Another fix for #187 now tested in a VE

File size: 33.1 KB
Line 
1#!/usr/bin/perl -w
2#
3# Creates common environment for distributions
4#
5# Copyright B. Cornec 2007-today
6# Eric Anderson's changes are (c) Copyright 2012 Hewlett Packard
7# Provided under the GPL v2
8#
9# $Id$
10#
11
12package ProjectBuilder::Distribution;
13
14use strict;
15use Data::Dumper;
16use Carp qw/cluck confess/;
17use ProjectBuilder::Version;
18use ProjectBuilder::Base;
19use ProjectBuilder::Conf;
20use File::Basename;
21use File::Copy;
22# requires perl 5.004 minimum in VM/VE
23use File::Compare;
24
25# Global vars
26# Inherit from the "Exporter" module which handles exporting functions.
27
28use vars qw(@ISA @EXPORT);
29use Exporter;
30
31# Export, by default, all the functions into the namespace of
32# any code which uses this module.
33
34our @ISA = qw(Exporter);
35our @EXPORT = qw(pb_distro_init pb_distro_conffile pb_distro_sysconffile pb_distro_api pb_distro_get pb_distro_get_if pb_distro_getlsb pb_distro_installdeps pb_distro_installpkgs pb_distro_getdeps pb_distro_only_deps_needed pb_distro_setuprepo pb_distro_setuposrepo pb_distro_setuprepo_gen pb_distro_get_context pb_distro_to_keylist pb_distro_conf_print pb_apply_conf_proxy);
36our ($VERSION,$REVISION,$PBCONFVER) = pb_version_init();
37
38=pod
39
40=head1 NAME
41
42ProjectBuilder::Distribution, part of the project-builder.org - module dealing with distribution detection
43
44=head1 DESCRIPTION
45
46This modules provides functions to allow detection of Linux distributions, and giving back some attributes concerning them.
47
48=head1 SYNOPSIS
49
50 use ProjectBuilder::Distribution;
51
52 #
53 # Return information on the running distro
54 #
55 my $pbos = pb_distro_get_context();
56 print "distro tuple: ".Dumper($pbos->name, $pbos->ver, $pbos->fam, $pbos->type, $pbos->pbsuf, $pbos->pbupd, $pbos->pbins, $pbos->arch)."\n";
57 #
58 # Return information on the requested distro
59 #
60 my $pbos = pb_distro_get_context("ubuntu-7.10-x86_64");
61 print "distro tuple: ".Dumper($pbos->name, $pbos->ver, $pbos->fam, $pbos->type, $pbos->pbsuf, $pbos->pbupd, $pbos->pbins, $pbos->arch)."\n";
62 #
63 # Return information on the running distro
64 #
65 my ($ddir,$dver) = pb_distro_guess();
66
67=head1 USAGE
68
69=over 4
70
71=item B<pb_distro_api>
72
73This function returns the mandatory configuration file used for api
74
75=cut
76
77sub pb_distro_api {
78
79return("CCCC/api.yaml");
80}
81
82
83=item B<pb_distro_conffile>
84
85This function returns the mandatory configuration file used for distribution/OS detection
86
87=cut
88
89sub pb_distro_conffile {
90
91if ($PBCONFVER < 1) {
92 return("CCCC/pb.conf");
93} else {
94 return("CCCC/pb.yml");
95}
96}
97
98=item B<pb_distro_sysconffile>
99
100This function returns the optional configuration file used for local customization
101
102=cut
103
104sub pb_distro_sysconffile {
105
106if ($PBCONFVER < 1) {
107 return("SSSS/pb.conf");
108} else {
109 return("SSSS/pb.yml");
110}
111}
112
113
114=item B<pb_distro_init>
115
116This function returns a hash of parameters indicating the distribution name, version, family, type of build system, suffix of packages, update command line, installation command line and architecture of the underlying Linux distribution. The value of the fields may be "unknown" in case the function was unable to recognize on which distribution it is running.
117
118As an example, Ubuntu and Debian are in the same "du" family. As well as RedHat, RHEL, CentOS, fedora are on the same "rh" family.
119Mandriva, Open SuSE and Fedora have all the same "rpm" type of build system. Ubuntu and Debian have the same "deb" type of build system.
120And "fc" is the extension generated for all Fedora packages (Version will be added by pb).
121All this information is stored in an external configuration file typically at /etc/pb/pb.yml
122
123When passing the distribution name and version as parameters, the B<pb_distro_init> function returns the parameter of that distribution instead of the underlying one.
124
125Cf: http://linuxmafia.com/faq/Admin/release-files.html
126Ideas taken from http://search.cpan.org/~kerberus/Linux-Distribution-0.14/lib/Linux/Distribution.pm
127
128=cut
129
130
131sub pb_distro_init {
132
133my $pbos = {
134 'name' => undef,
135 'version' => undef,
136 'arch' => undef,
137 'family' => "unknown",
138 'suffix' => "unknown",
139 'update' => "unknown",
140 'install' => "unknown",
141 'type' => "unknown",
142 'os' => "unknown",
143 'nover' => "false",
144 'rmdot' => "false",
145 'useminor' => "false",
146 };
147$pbos->{'name'} = shift;
148$pbos->{'version'} = shift;
149$pbos->{'arch'} = shift;
150
151# Adds conf file for distribution description
152# the location of the conf file is finalyzed at install time
153# depending whether we deal with package install or tar file install
154
155pb_conf_add(pb_distro_sysconffile());
156
157# Similarly for the local file available for sysadmin. After the previous one to allow overwrite to work
158pb_conf_add(pb_distro_conffile());
159
160# If we don't know which distribution we're on, then guess it
161($pbos->{'name'},$pbos->{'version'}) = pb_distro_guess() if ((not defined $pbos->{'name'}) || (not defined $pbos->{'version'}));
162
163# For some rare cases, typically nover ones
164$pbos->{'name'} = "unknown" if (not defined $pbos->{'name'});
165$pbos->{'version'} = "unknown" if (not defined $pbos->{'version'});
166
167# Initialize arch
168$pbos->{'arch'} = pb_get_arch() if (not defined $pbos->{'arch'});
169# Solves a bug on Red Hat 6.x where real arch is not detected when using setarch and a chroot
170# As it was only i386 forcing it here.
171$pbos->{'arch'} = "i386" if (($pbos->{'name'} eq "redhat") && ($pbos->{'version'} =~ /^6\./));
172
173# Dig into the tuple to find the best answer
174# Do NOT factorize here, as it won't work as of now for hash creation
175# Do NOT change order without caution
176$pbos->{'useminor'} = pb_distro_get($pbos,"osuseminorrel");
177$pbos->{'family'} = pb_distro_get($pbos,"osfamily");
178$pbos->{'type'} = pb_distro_get($pbos,"ostype");
179($pbos->{'os'},$pbos->{'install'},$pbos->{'update'}) = pb_distro_get($pbos,("os","osins","osupd"));
180($pbos->{'localinstall'},$pbos->{'nover'},$pbos->{'rmdot'},$pbos->{'suffix'}) = pb_distro_get_if($pbos,"oslocalins","osnover","osremovedotinver","ossuffix");
181
182# Some OS have no interesting version
183$pbos->{'version'} = "nover" if ((defined $pbos->{'nover'}) && ($pbos->{'nover'} eq "true"));
184
185# For some OS remove the . in version name for extension
186my $dver2 = $pbos->{'version'};
187$dver2 =~ s/\.//g if ((defined $pbos->{'rmdot'}) && ($pbos->{'rmdot'} eq "true"));
188
189if ((not defined $pbos->{'suffix'}) || ($pbos->{'suffix'} eq "")) {
190 # By default suffix is a concatenation of name and version
191 $pbos->{'suffix'} = ".$pbos->{'name'}$dver2"
192} else {
193 # concat just the version to what has been found
194 $pbos->{'suffix'} = ".$pbos->{'suffix'}$dver2";
195}
196
197# if ($arch eq "x86_64") {
198# $opt="--exclude=*.i?86";
199# }
200pb_log(2,"DEBUG: pb_distro_init: ".Dumper($pbos)."\n");
201
202return($pbos);
203}
204
205=item B<pb_distro_guess>
206
207This function returns a list of 2 parameters indicating the distribution name and version of the underlying Linux distribution. The value of those 2 fields may be "unknown" in case the function was unable to recognize on which distribution it is running.
208
209On my home machine it would currently report ("mandriva","2010.2").
210
211=cut
212
213sub pb_distro_guess {
214
215# 1: List of files that unambiguously indicates what distro we have
216# 2: List of files that ambiguously indicates what distro we have
217# 3: Should have the same keys as the previous one. If ambiguity, which other distributions should be checked
218# 4: Matching Rg. Expr to detect distribution and version
219my ($single_rel_files, $ambiguous_rel_files,$distro_similar,$distro_match,$nover) = pb_conf_get("osrelfile","osrelambfile","osambiguous","osrelexpr","osnover");
220
221my $release;
222my $distro;
223
224# Begin to test presence of non-ambiguous files
225# that way we reduce the choice
226my ($d,$r);
227while (($d,$r) = each %$single_rel_files) {
228 if (defined $ambiguous_rel_files->{$d}) {
229 print STDERR "The key $d is considered as both unambiguous and ambiguous.\n";
230 confess "Please fix your configuration file.\n"
231 }
232 if (-f "$r" && ! -l "$r") {
233 my $tmp=pb_get_content("$r");
234 # Found the only possibility.
235 # Try to get version and return
236 if (defined ($distro_match->{$d})) {
237 ($release) = $tmp =~ m/$distro_match->{$d}/m;
238 } else {
239 if (not defined ($nover->{$d})) {
240 print STDERR "Unable to find $d version in $r (non-ambiguous)\n";
241 confess "Please report to the maintainer bruno_at_project-builder.org\n";
242 }
243 $release = "unknown";
244 }
245 return($d,$release);
246 }
247}
248
249# Now look at ambiguous files
250# Ubuntu before 10.04 includes a /etc/debian_version file that creates an ambiguity with debian
251# So we need to look at distros in reverse alphabetic order to treat ubuntu always first via lsb
252my $found = 0;
253foreach $d (reverse keys %$ambiguous_rel_files) {
254 $r = $ambiguous_rel_files->{$d};
255 if (-f "$r" && !-l "$r") {
256 # Found one possibility.
257 # Get all distros concerned by that file
258 my $tmp = pb_get_content("$r");
259 my $ptr = $distro_similar->{$d};
260 pb_log(2,"amb: ".Dumper($ptr)."\n");
261 $release = "unknown";
262 foreach my $dd (split(/,/,$ptr)) {
263 pb_log(2,"check $dd\n");
264 # Try to check pattern
265 if (defined $distro_match->{$dd}) {
266 pb_log(2,"cmp: $distro_match->{$dd} - vs - $tmp\n");
267 ($release) = $tmp =~ m/$distro_match->{$dd}/m;
268 if ((defined $release) && ($release ne "unknown")) {
269 $distro = $dd;
270 $found = 1;
271 last;
272 }
273 }
274 }
275 last if ($found == 1);
276 }
277}
278if ($found == 0) {
279 #
280 # Now look at the os-release file to see if we have a std distribution description
281 #
282 foreach my $r ("/usr/lib/os-release","/etc/os-release") {
283 if (-r $r) {
284 my $tmp = pb_get_content("$r");
285 ($release) = $tmp =~ m/.*\nVERSION_ID=[\"\']*([0-9a-z\._-]+)[\"\']*\n/m;
286 ($distro) = $tmp =~ m/.*\nID=[\"\']*([0-9A-z\._-]+)[\"\']*\n/m;
287 # Remove the leap suffix if present (OpenSUSE)
288 $distro =~ s/-leap//;
289 if ((defined $release) && (defined $distro)) {
290 $found = 1;
291 last;
292 }
293 }
294 }
295}
296if ($found == 0) {
297 print STDERR "Unable to find a version in ".join(' ',keys %$ambiguous_rel_files)." (ambiguous)\n";
298 confess "Please report to the maintainer bruno_at_project-builder.org\n";
299} else {
300 return($distro,$release);
301}
302}
303
304=item B<pb_distro_getlsb>
305
306This function returns the 5 lsb values LSB version, distribution ID, Description, release and codename.
307As entry it takes an optional parameter to specify whether the output is short or not.
308
309=cut
310
311sub pb_distro_getlsb {
312
313my $s = shift;
314pb_log(3,"Entering pb_distro_getlsb\n");
315
316my ($ambiguous_rel_files) = pb_conf_get("osrelambfile");
317my $lsbf = $ambiguous_rel_files->{"lsb"};
318
319# LSB has not been configured.
320if (not defined $lsbf) {
321 print STDERR "no lsb entry defined for osrelambfile\n";
322 confess "You modified upstream delivery and lost !\n";
323}
324
325if (-r $lsbf) {
326 my $rep = pb_get_content($lsbf);
327 # Create elementary fields
328 my ($c, $r, $d, $i, $l) = ("", "", "", "", "");
329 for my $f (split(/\n/,$rep)) {
330 pb_log(3,"Reading file part ***$f***\n");
331 $c = $f if ($f =~ /^DISTRIB_CODENAME/);
332 $c =~ s/DISTRIB_CODENAME=/Codename:\t/;
333 $r = $f if ($f =~ /^DISTRIB_RELEASE/);
334 $r =~ s/DISTRIB_RELEASE=/Release:\t/;
335 $d = $f if ($f =~ /^DISTRIB_DESCRIPTION/);
336 $d =~ s/DISTRIB_DESCRIPTION=/Description:\t/;
337 $d =~ s/"//g;
338 $i = $f if ($f =~ /^DISTRIB_ID/);
339 $i =~ s/DISTRIB_ID=/Distributor ID:\t/;
340 $l = $f if ($f =~ /^LSB_VERSION/);
341 $l =~ s/LSB_VERSION=/LSB Version:\t/;
342 }
343 my $regexp = "^[A-z ]*:[\t ]*";
344 $c =~ s/$regexp// if (defined $s);
345 $r =~ s/$regexp// if (defined $s);
346 $d =~ s/$regexp// if (defined $s);
347 $i =~ s/$regexp// if (defined $s);
348 $l =~ s/$regexp// if (defined $s);
349 return($l, $i, $d, $r, $c);
350} else {
351 print STDERR "Unable to read $lsbf file\n";
352 confess "Please report to the maintainer bruno_at_project-builder.org\n";
353}
354}
355
356# Internal function
357
358sub pb_apply_conf_proxy {
359my ($pbos) = @_;
360
361my $ftp_proxy = pb_distro_get_if($pbos,"ftp_proxy");
362my $http_proxy = pb_distro_get_if($pbos,"http_proxy");
363my $https_proxy = pb_distro_get_if($pbos,"https_proxy");
364
365# We do not overwrite shell settings
366$ENV{'ftp_proxy'} ||= $ftp_proxy if ((defined $ftp_proxy) && ($ftp_proxy ne ""));
367$ENV{'http_proxy'} ||= $http_proxy if ((defined $http_proxy) && ($http_proxy ne ""));
368$ENV{'https_proxy'} ||= $https_proxy if ((defined $https_proxy) && ($https_proxy ne ""));
369}
370
371=item B<pb_distro_installpkgs>
372
373This function install the packages passed as parameters on a distribution.
374
375=cut
376
377sub pb_distro_installpkgs {
378
379my $pbos = shift;
380my $pkgs = shift; # list of pkgs to install
381my $local = shift; # optional should we install local packages or remote (for deb command is different)
382
383# Protection
384confess "Missing install command for $pbos->{name}-$pbos->{version}-$pbos->{arch}" unless (defined $pbos->{install} && $pbos->{install} =~ /\w/);
385pb_apply_conf_proxy($pbos);
386pb_log(1, "ftp_proxy=$ENV{'ftp_proxy'}\n") if (defined $ENV{'ftp_proxy'});
387pb_log(1, "http_proxy=$ENV{'http_proxy'}\n") if (defined $ENV{'http_proxy'});
388pb_log(1, "https_proxy=$ENV{'https_proxy'}\n") if (defined $ENV{'https_proxy'});
389
390my $npkgs = "";
391foreach my $p (split(/ /,$pkgs)) {
392 $npkgs .= "'".$p."' "
393 }
394
395# This may not be // proof. We should test for availability of repo and sleep if not
396my $cmd = "$pbos->{'install'} $npkgs";
397$cmd = "$pbos->{'localinstall'} $npkgs" if ((defined $local) && (defined $pbos->{'localinstall'}) && ($pbos->{'localinstall'} !~ /[ ]*/));
398my $ret = pb_system($cmd, "Installing packages ($cmd)","mayfail");
399# Try to accomodate deficient proxies
400if ($ret != 0) {
401 $ret = pb_system($cmd, "Re-trying installing packages ($cmd)");
402}
403confess "Some packages did not install" if (($ret != 0) && ($Global::pb_stop_on_error));
404}
405
406
407=item B<pb_distro_installdeps>
408
409This function install the dependencies required to build the package on a distro.
410If $forcerepo is defined then do not assume packages are alredy installed, but reinstall them
411(useful if you add a repo which contains more up to date packages that you need)
412Dependencies can be passed as the 4th parameter in which case they are not computed
413
414=cut
415
416sub pb_distro_installdeps {
417
418# SPEC file
419my $f = shift;
420my $pbos = shift;
421my $forcerepo = shift;
422my $deps = shift; # optional list of deps to install
423my $local = shift; # optional should we install local packages or remote (for deb command is different)
424my $deps2;
425
426if (not defined $deps) {
427 $deps = pb_distro_getdeps($f,$pbos, $forcerepo);
428}
429pb_log(2,"DEBUG: Packages to install: $deps\n") if (defined $deps);
430return if ((not defined $deps) || ($deps =~ /^\s*$/));
431
432pb_distro_installpkgs($pbos,$deps,$local);
433# Check that all deps have been installed correctly
434# This time we don't forcerepo to avoid getting a list as a return as we have
435# already forced it previously, and this time we just want to check
436$deps = pb_distro_getdeps($f, $pbos, undef);
437confess "Some dependencies did not install ($deps)" if ((defined $deps) && ($deps =~ /\S/) && ($Global::pb_stop_on_error));
438}
439
440=item B<pb_distro_getdeps>
441
442This function computes the dependencies indicated in the build file and return them as a string of packages to install
443
444=cut
445
446sub pb_distro_getdeps {
447
448my $f = shift;
449my $pbos = shift;
450my $forcerepo = shift;
451
452my $regexp = "";
453my $deps = "";
454my $sep = $/;
455
456# Protection
457return("") if (not defined $pbos->{'type'});
458return("") if (not defined $f);
459
460pb_log(3,"entering pb_distro_getdeps: $pbos->{'type'} - $f\n");
461if ($pbos->{'type'} eq "rpm") {
462 # In RPM this could include files, but we do not handle them atm.
463 $regexp = '^BuildRequires:(.*)$';
464} elsif ($pbos->{'type'} eq "deb") {
465 $regexp = '^Build-Depends:(.*)$';
466} elsif ($pbos->{'type'} eq "aur") {
467 $regexp = '^makedepends=(.*)$';
468} elsif ($pbos->{'type'} eq "apk") {
469 $regexp = '^makedepends=(.*)$';
470} elsif ($pbos->{'type'} eq "ebuild") {
471 $sep = '"'.$/;
472 $regexp = '^DEPEND="(.*)"\n';
473} else {
474 # No idea
475 return("");
476}
477pb_log(2,"regexp: $regexp\n");
478
479# Preserve separator before using the one we need
480my $oldsep = $/;
481$/ = $sep;
482open(DESC,"$f") || (cluck "Unable to open $f" && return(""));
483while (<DESC>) {
484 pb_log(4,"read: $_\n");
485 next if (! /$regexp/);
486 chomp();
487
488 my $nextline;
489 # Support multi-lines deps for .deb
490 if ($pbos->{type} eq 'deb') {
491 while ($nextline = <DESC>) {
492 last unless $nextline =~ /^\s+(.+)$/o;
493 $_ .= $1;
494 }
495 }
496
497 # What we found with the regexp is the list of deps.
498 pb_log(2,"found deps: $_\n");
499 s/$regexp/$1/i;
500 pb_log(4,"found deps 1: $_\n");
501 # Remove conditions in the middle and at the end for deb
502 s/\([><=]+[^,]*,/,/g;
503 pb_log(4,"found deps 2: $_\n");
504 s/\([><=]+[^,]*$//g;
505 pb_log(4,"found deps 3: $_\n");
506 # Same for rpm
507 s/[><=]+[^,]*,/,/g;
508 pb_log(4,"found deps 4: $_\n");
509 s/[><=]+.*$//g;
510 pb_log(4,"found deps 5: $_\n");
511 # Improve string format (remove , and spaces at start, end and in double
512 s/,/ /g;
513 pb_log(4,"found deps 6: $_\n");
514 s/^\s*//;
515 pb_log(4,"found deps 7: $_\n");
516 # $ here removes the \n
517 s/\s*$//;
518 pb_log(4,"found deps 8: $_\n");
519 s/\s+/ /g;
520 pb_log(4,"found deps 9: $_\n");
521 $deps .= " ".$_;
522 pb_log(4,"found deps end: $deps\n");
523
524 # Support multi-lines deps for .deb (fwup)
525 if (defined $nextline) {
526 $_ = $nextline;
527 redo;
528 }
529}
530close(DESC);
531$/ = $oldsep;
532pb_log(2,"now deps: $deps\n");
533if (defined $forcerepo) {
534 # We want to force installation of all pkgs
535 # because a repo was setup in between, which may contains updated versions
536 pb_log(0,"Forcing installation of all packages due to previous repo setup\n");
537 return($deps);
538} else {
539 pb_log(0,"Installation of only necessary packages\n");
540 my $deps2 = pb_distro_only_deps_needed($pbos,$deps);
541 return($deps2);
542}
543}
544
545
546=item B<pb_distro_only_deps_needed>
547
548This function returns only the dependencies not yet installed
549
550=cut
551
552sub pb_distro_only_deps_needed {
553
554my $pbos = shift;
555my $deps = shift;
556
557return("") if ((not defined $deps) || ($deps =~ /^\s*$/));
558my $deps2 = "";
559# Avoid to install what is already there
560delete $ENV{'COLUMNS'};
561foreach my $p (split(/\s+/,$deps)) {
562 next if $p =~ /^\s*$/o;
563 if ($pbos->{'type'} eq "rpm") {
564 my $rpmcmd = "rpm -q --whatprovides --quiet";
565 # whatprovides doesn't work for RH6.2
566 $rpmcmd = "rpm -q --quiet" if (($pbos->{'name'} eq "redhat") && ($pbos->{'version'} =~ /6/));
567 my $res = pb_system("$rpmcmd '".$p."'","Looking for $p","mayfail");
568 next if ($res eq 0);
569 pb_log(1, "INFO: missing dependency $p\n");
570 } elsif ($pbos->{'type'} eq "deb") {
571 my $res = pb_system("dpkg -L $p","Looking for $p","mayfail");
572 next if ($res eq 0);
573 open(CMD,"dpkg -l $p |") || (cluck "Unable to run dpkg -l $p: $!" && next);
574 my $ok = 0;
575 while (<CMD>) {
576 $ok = 1 if /^ii\s+$p/;
577 }
578 close(CMD);
579 next if $ok;
580 pb_log(1, "INFO: missing dependency $p\n");
581 } elsif ($pbos->{'type'} eq "ebuild") {
582 } elsif ($pbos->{'type'} eq "apk") {
583 my $res = pb_system("apk -e info $p","Looking for $p","mayfail");
584 next if ($res eq 0);
585 pb_log(1, "INFO: missing dependency $p\n");
586 } else {
587 # Not reached
588 }
589 $deps2 .= " $p";
590}
591
592$deps2 =~ s/^\s*//;
593pb_log(2,"List of missing packages: $deps2\n");
594return($deps2);
595}
596
597# Internal
598sub pb_distro_compare_repo {
599
600my $src = shift;
601my $dest = shift;
602
603if (not -f $dest) {
604 pb_log(1, "INFO: Creating new file $dest\n");
605} elsif (-f $dest && -s $dest == 0) {
606 pb_log(1, "INFO: Overwriting empty file $dest\n");
607} elsif (-f $dest && compare("$src", $dest) == 0) {
608 pb_log(1, "INFO: Overwriting identical file $dest\n");
609} else {
610 pb_log(0, "ERROR: destination file $dest exists and is different than source $src\n");
611 pb_system("cat $dest","INFO: Dest...\n","verbose");
612 pb_system("cat $src","INFO: New...\n","verbose");
613 pb_log(1, "INFO: Returning...\n");
614 return(1);
615}
616return(0);
617}
618
619
620=item B<pb_distro_setuposrepo>
621
622This function sets up potential additional repository for the setup phase
623
624=cut
625
626sub pb_distro_setuposrepo {
627
628my $pbos = shift;
629
630pb_log(3, "INFO: Adding osrepo from config file\n");
631my %h;
632my $h = \%h;
633# Adds conf file for availability of conf elements either from the local build env or from a VE/VM/RM in which the conf file has been passed
634$h = pb_conf_cache(pb_distro_conffile(),$h);
635#my $repo = pb_distro_get_param($pbos,$osrepo);
636my $repo = pb_distro_get_in_hash_if($pbos,$h,"osrepo");
637return(pb_distro_setuprepo_gen($pbos,$repo));
638}
639
640=item B<pb_distro_setuprepo>
641
642This function sets up potential additional repository to the build/install/test environment
643If done, it returns forcerepo if a repo was added, if not undef
644
645=cut
646
647sub pb_distro_setuprepo {
648
649my $pbos = shift;
650my $repotype = shift;
651
652my %h;
653my $h = \%h;
654# Adds conf file for availability of conf elements either from the local build env or from a VE/VM/RM in which the conf file has been passed
655$h = pb_conf_cache("$ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.yml",$h) if ((defined $ENV{'PBROOTDIR'}) && (-f "$ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.yml"));
656$h = pb_conf_cache("$ENV{'PBDESTDIR'}/pbrc.yml",$h) if ((defined $ENV{'PBDESTDIR'}) && (-f "$ENV{'PBDESTDIR'}/pbrc.yml"));
657
658my $repo = pb_distro_get_in_hash_if($pbos,$h,"add".$repotype."repo");
659# If no repo then set it up as undef so the return value is correct from pb_distro_setuprepo_gen
660$repo = undef if ((defined $repo) && ($repo eq ""));
661if ($repotype =~ /install/) {
662 # Give a probable default if nothing is provided to avoid overloading conf files
663 #
664 if (not defined $repo) {
665 my ($pbrepo) = pb_conf_get_in_hash_if($h,"pbrepo");
666 if (not defined $pbrepo) {
667 cluck("No pbrepo parameter defined in your project configuration file, please fix !");
668 return(undef);
669 }
670 my $url = "$pbrepo->{$ENV{'PBPROJ'}}";
671 my ($testver,$delivery) = pb_conf_get_in_hash_if($h,"testver","delivery");
672 $delivery->{$ENV{'PBPROJ'}} = "" if (not defined $delivery->{$ENV{'PBPROJ'}});
673 $url .= "/$delivery->{$ENV{'PBPROJ'}}/";
674 my $repotag = "";
675 $repotag = "-$delivery->{$ENV{'PBPROJ'}}" if ($delivery->{$ENV{'PBPROJ'}} ne "");
676 $url .= "/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/$ENV{PBPROJ}$repotag.";
677 my $ext = "";
678 if ($pbos->{'type'} eq "rpm") {
679 $ext = "repo";
680 if ($pbos->{'family'} eq "md") {
681 $ext = "addmedia";
682 }
683 } elsif ($pbos->{'type'} eq "deb") {
684 $ext = ".sources.list";
685 }
686 $repo = $url.$ext;
687 }
688}
689pb_log(1, "INFO: Adding $repo from config file for $repotype step\n") if (defined $repo);
690return(pb_distro_setuprepo_gen($pbos,$repo));
691}
692
693=item B<pb_distro_setuprepo_gen>
694
695This functionthe sets up in a generic way potential additional repository passed as a param
696It returns forcerepo if one was added, else undef
697
698=cut
699
700sub pb_distro_setuprepo_gen {
701
702my $pbos = shift;
703my $param = shift;
704
705return(undef) if (not defined $param);
706
707pb_apply_conf_proxy($pbos);
708
709# Loop on the list of additional repo
710foreach my $i (split(/,/,$param)) {
711
712 pb_log(1,"Adding repository defined by $i\n");
713 my ($scheme, $account, $host, $port, $path) = pb_get_uri($i);
714 my $bn = basename($i);
715
716 # The repo file can be local or remote. download or copy at the right place
717 if (($scheme eq "ftp") || ($scheme =~ /http/)) {
718 pb_system("wget -O $ENV{'PBTMP'}/$bn $i","Downloading additional repository file $i");
719 } else {
720 copy($i,"$ENV{'PBTMP'}/$bn");
721 }
722
723 # The repo file can be a real file or a package
724 if ($pbos->{'type'} eq "rpm") {
725 if ($bn =~ /\.rpm$/) {
726 my $pn = $bn;
727 $pn =~ s/\.rpm//;
728 if (pb_system("rpm -q --quiet $pn","","mayfail") != 0) {
729 pb_system("sudo rpm -Uvh $ENV{'PBTMP'}/$bn","Adding package $bn to setup repository");
730 }
731 } elsif ($bn =~ /\.repo$/) {
732 my $dirdest = "";
733 my $reponame = "";
734 # TODO: could go in pb.yml in fact
735 if ($pbos->{install} =~ /\byum\b/) {
736 $reponame="yum";
737 $dirdest = "/etc/yum.repos.d";
738 } elsif ($pbos->{install} =~ /\bdnf\b/) {
739 $reponame="dnf";
740 $dirdest = "/etc/yum.repos.d";
741 } elsif ($pbos->{install} =~ /\bzypper\b/) {
742 $reponame="zypper";
743 $dirdest = "/etc/zypp/repos.d";
744 } else {
745 cluck "Unknown location for repository file for '$pbos->{install}' command";
746 next;
747 }
748 my $dest = "$dirdest/$bn";
749 return(undef) if (pb_distro_compare_repo("$ENV{'PBTMP'}/$bn",$dest) == 1);
750 if (! -d $dirdest) {
751 cluck "Missing directory $dirdest ($reponame)";
752 return(undef);
753 }
754 pb_system("sudo mv $ENV{'PBTMP'}/$bn $dest","Adding $reponame repository") if (not -f "$dest");
755 # OpenSUSE does't seem to import keys automatically
756 # :-(
757 if ($pbos->{install} =~ /\bzypper\b/) {
758 my $keyfile = undef;
759 open(REPO,"$dest") || (cluck "Unable to open $dest" && next);
760 while (<REPO>) {
761 $keyfile = $_;
762 if ($keyfile =~ /^gpgkey=/) {
763 $keyfile =~ s/gpgkey=//;
764 last;
765 }
766 }
767 close(REPO);
768 if (defined $keyfile) {
769 pb_system("wget -O $ENV{'PBTMP'}/$bn $keyfile","Downloading GPG key file $keyfile");
770 pb_system("sudo rpm --import $ENV{'PBTMP'}/$bn","Importing GPG key file $keyfile");
771 unlink("$ENV{'PBTMP'}/$bn");
772 }
773 }
774 } elsif ($bn =~ /\.addmedia/) {
775 # URPMI repo
776 # We should test that it's not an already setup urpmi repo
777 open(URPMI,"/etc/urpmi/urpmi.cfg") || (cluck "Unable to open /etc/urpmi/urpmi.cfg" && next);
778 my $found = 0;
779 my $entry = $bn;
780 $entry =~ s/.addmedia$//;
781 while (<URPMI>) {
782 $found = 1 if ($_ =~ /^$entry /);
783 }
784 pb_system("chmod 755 $ENV{'PBTMP'}/$bn ; sudo $ENV{'PBTMP'}/$bn 2>&1 > /dev/null","Adding urpmi repository") if ($found == 0);
785 pb_log(0,"INFO urpmi $bn already set up\n") if ($found == 1);
786 } else {
787 pb_log(0,"ERROR: Unable to deal with repository file $i on rpm distro ! Please report to dev team\n");
788 }
789 } elsif ($pbos->{'type'} eq "deb") {
790 if ($bn =~ /\.sources.list$/) {
791 my $dest = "/etc/apt/sources.list.d/$bn";
792 return(undef) if (pb_distro_compare_repo("$ENV{'PBTMP'}/$bn",$dest) == 1);
793 pb_system("sudo mv $ENV{'PBTMP'}/$bn $dest","Adding apt repository $dest");
794 # Check whether GPG keys for this repo are already known and if
795 # not add them
796 open(REPO,"$dest") || (cluck "Unable to open $dest" && next);
797 my $debrepo;
798 while (<REPO>) {
799 if (/^deb\s/) {
800 $debrepo = $_;
801 chomp($debrepo);
802 $debrepo =~ s|^deb ([^\s]+)\s([^\s]+)\s([^\s]+)|$1/dists/$2|;
803 last;
804 }
805 }
806 close(REPO);
807 return(undef) if (not defined $debrepo);
808
809 pb_system("wget -O $ENV{'PBTMP'}/Release $debrepo/Release","Downloading $debrepo/Release");
810 pb_system("wget -O $ENV{'PBTMP'}/Release.gpg $debrepo/Release.gpg","Downloading $debrepo/Release.gpg");
811 my $signature = undef;
812 my ($pbgpgserver) = pb_conf_get("pbgpgserver");
813 confess "Unable to find a GPG server in configuration, please define pbgpgserver" if (not defined $pbgpgserver);
814 my $keyserver = $pbgpgserver->{$ENV{'PBPROJ'}};
815 $keyserver = $pbgpgserver->{'default'} if (not defined $keyserver);
816 confess "Unable to find a GPG server in configuration, please define correctly pbgpgserver" if (not defined $keyserver);
817 open(SIGN,"LANGUAGE=C LANG=C gpg --verify --keyid-format=long $ENV{'PBTMP'}/Release.gpg $ENV{'PBTMP'}/Release 2>&1 |") || (cluck "Unable to verify GPG signature from Release.gpg\n" && next);
818 while(<SIGN>) {
819 chomp();
820 if (/^gpg: .*key /) {
821 $signature = $_;
822 $signature =~ s/^gpg: .*key [ID ]*([A-Z0-9]+)/$1/;
823 pb_system("gpg --recv-keys --keyserver $keyserver $signature","Importing GPG signature for $signature");
824 last;
825 }
826 }
827 close(SIGN);
828 return(undef) if (not defined $signature);
829
830 pb_log(3, "GnuPG repo verify returned: $signature\n");
831 pb_system("gpg -a --export -o $ENV{'PBTMP'}/apt.sig \'$signature\'","Exporting GnuPG signature of $signature");
832 pb_system("sudo apt-key add $ENV{'PBTMP'}/apt.sig","Adding GnuPG signature of $signature to APT key ring");
833 pb_system("sudo apt-get update","Updating apt repository");
834 } else {
835 pb_log(0,"ERROR: Unable to deal with repository file $i on deb distro ! Please report to dev team\n");
836 }
837 } else {
838 pb_log(0,"ERROR: Unable to deal with repository file $i on that distro ! Please report to dev team\n");
839 }
840}
841return("forcerepo");
842}
843
844=item B<pb_distro_to_keylist>
845
846Given a pbos object (first param) and the generic key (second param), get the list of possible keys for looking up variable for
847filter names. The list will be sorted most-specific to least specific.
848
849=cut
850
851sub pb_distro_to_keylist ($$) {
852
853my ($pbos, $generic) = @_;
854
855foreach my $key (qw/name version arch family type os/) {
856 confess "missing pbos key $key" unless (defined $pbos->{$key});
857}
858
859my @keylist = ("$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}", "$pbos->{'name'}-$pbos->{'version'}");
860
861# Loop to include also previous minor versions
862# if configured so
863if ((defined $pbos->{'useminor'}) && ($pbos->{'useminor'} eq "true") && ($pbos->{'version'} =~ /^(\d+)\.(\d+)$/o)) {
864 my ($major, $minor) = ($1, $2);
865 while ($minor > 0) {
866 $minor--;
867 push (@keylist, "$pbos->{'name'}-${major}.$minor");
868 }
869 push (@keylist, "$pbos->{'name'}-$major");
870}
871
872push (@keylist, $pbos->{'name'}, $pbos->{'family'}, $pbos->{'type'}, $pbos->{'os'}, $generic);
873return @keylist;
874}
875
876=item B<pb_distro_get_param>
877
878This internal function gets the parameters in the conf file from the most precise tuple up to default
879
880=cut
881
882sub pb_distro_get_param {
883
884my $pbos = shift;
885my $var = shift;
886my @param = ();
887my $i = 0;
888pb_log(3,"var: ".Dumper($var));
889
890my @keylist = pb_distro_to_keylist($pbos,"default");
891pb_log(3,"keylist: ".Dumper(@keylist));
892
893my $p = $var->{"ptr"};
894foreach my $opt (@$p) {
895 pb_log(3,'opt: '.Dumper($opt));
896 my $param = undef;
897 my $fkey = undef;
898 foreach my $key (@keylist) {
899 pb_log(3,"key: $key\n");
900 if (defined $opt->{$key}) {
901 $param = $opt->{$key};
902 $fkey = $key;
903 last;
904 }
905 }
906 my $field = $var->{"val"};
907 pb_log(3,"field: ".Dumper($field));
908 $fkey = "default" if (not defined $fkey);
909 pb_log(3,"key: $fkey\n");
910 pb_log(2,"DEBUG: pb_distro_get_if on $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'} for $field->[$i]".'{'.$fkey.'}'." returns ");
911 if (defined $param) {
912 # Allow replacement of variables inside the parameter such as name, version, arch for rpmbootstrap
913 # but not shell variable which are backslashed
914 if ($param =~ /[^\\]\$/) {
915 pb_log(3,"Expanding variable on $param\n");
916 eval { $param =~ s/(\$\w+->\{\'\w+\'\})/$1/eeg };
917 }
918 pb_log(2,"$param\n");
919 } else {
920 pb_log(2,"undefined\n");
921 }
922 push @param,$param;
923 $i++;
924}
925
926# Return one param if user only asked for one lookup, an array if not.
927my $nb = @param;
928pb_log(3,"Param".Dumper(@param)." has $nb members\n");
929if ($nb eq 1) {
930 pb_log(3,"Return param $param[0]\n") if (defined $param[0]);
931 return($param[0]);
932} else {
933 return(@param);
934}
935}
936
937
938=item B<pb_distro_get_if>
939
940This function gets the parameters in the conf file from the most precise tuple up to default
941
942=cut
943
944sub pb_distro_get_if {
945
946my $pbos = shift;
947my @ptr = pb_conf_get_if(@_);
948my $var;
949$var->{"ptr"} = \@ptr;
950$var->{"val"} = \@_;
951return(pb_distro_get_param($pbos,$var));
952}
953
954=item B<pb_distro_get>
955
956This function gets the parameters in the conf file from the most precise tuple up to default.
957Aborts of one param doesn't exist whereas it should
958
959=cut
960
961sub pb_distro_get {
962
963my $pbos = shift;
964my @param = @_;
965my @return = pb_distro_get_if($pbos,@param);
966
967foreach my $i (0..$#param) {
968 confess "No $param[$i] defined for $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}" if (not defined $return[$i]);
969}
970my $nb = @return;
971if ($nb eq 1) {
972 return($return[0]);
973} else {
974 return(@return);
975}
976}
977
978=item B<pb_distro_get_in_hash_if>
979
980This function gets the parameters in the conf file passed as hash from the most precise tuple up to default
981
982=cut
983
984sub pb_distro_get_in_hash_if {
985
986my $pbos = shift;
987my $lh = shift || return(());
988
989my @ptr = pb_conf_get_in_hash_if($lh,@_);
990my $var;
991$var->{"ptr"} = \@ptr;
992$var->{"val"} = \@_;
993return(pb_distro_get_param($pbos,$var));
994}
995
996=item B<pb_distro_get_context>
997
998This function gets the OS context passed as parameter and return the corresponding distribution hash
999If passed undef or "" then auto-detects
1000
1001=cut
1002
1003
1004sub pb_distro_get_context {
1005
1006my $os = shift;
1007my $pbos;
1008
1009if ((defined $os) && ($os ne "")) {
1010 my ($name,$ver,$darch) = split(/-/,$os);
1011 pb_log(0,"Bad format for $os") if ((not defined $name) || (not defined $ver) || (not defined $darch)) ;
1012 chomp($darch);
1013 $pbos = pb_distro_init($name,$ver,$darch);
1014} else {
1015 $pbos = pb_distro_init();
1016}
1017return($pbos);
1018}
1019
1020=item B<pb_distro_conf_print>
1021
1022This function prints every configuration parameter in order to help debug stacking issues with conf files. If a VM/VE/RM is given, restrict display to this distribution. If parameters are passed, restrict again the display to these values only.
1023
1024=cut
1025
1026sub pb_distro_conf_print {
1027
1028my $pbos = shift;
1029my @keys = @_;
1030my $all = 0;
1031
1032if ($#keys == -1) {
1033 pb_log(0,"Full pb configuration for project $ENV{'PBPROJ'}\n");
1034 pb_log(0,"================================================\n");
1035 @keys = pb_conf_get_all();
1036 $all = 1;
1037}
1038if (defined $ENV{'PBV'}) {
1039 pb_log(1,"Distribution $ENV{'PBV'}\n");
1040 pb_log(1,"========================\n");
1041} else {
1042 pb_log(1,"Local Distribution\n");
1043 pb_log(1,"==================\n");
1044}
1045
1046my %rep;
1047my $i = 0;
1048# Index on distro
1049foreach my $r (pb_distro_get_if($pbos,@keys)) {
1050 $rep{$keys[$i]} = $r if (defined $keys[$i]);
1051 $i++;
1052}
1053$i = 0;
1054# Then Index on prj to overwrite previous value if needed
1055foreach my $r (pb_conf_get_if(@keys)) {
1056 $rep{$keys[$i]} = $r->{'default'} if (defined $r->{'default'});
1057 $rep{$keys[$i]} = $r->{$ENV{'PBPROJ'}} if (defined $r->{$ENV{'PBPROJ'}});
1058 $i++;
1059}
1060foreach my $r (keys %rep) {
1061 pb_log(1, "$r => ");
1062 pb_log(0, "$rep{$r}\n") if (($all == 0) && (defined $rep{$r}));
1063 pb_log(0, "$r = $rep{$r}\n") if (($all == 1) && (defined $rep{$r}));
1064}
1065}
1066
1067
1068=back
1069
1070=head1 WEB SITES
1071
1072The 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/>.
1073
1074=head1 USER MAILING LIST
1075
1076None exists for the moment.
1077
1078=head1 AUTHORS
1079
1080The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
1081
1082=head1 COPYRIGHT
1083
1084Project-Builder.org is distributed under the GPL v2.0 license
1085described in the file C<COPYING> included with the distribution.
1086
1087=cut
1088
1089
10901;
Note: See TracBrowser for help on using the repository browser.