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

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

Fix #187 by protecting package requirements which may contain parenthesis e.g.

File size: 33.0 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
390# This may not be // proof. We should test for availability of repo and sleep if not
391my $cmd = "$pbos->{'install'} $pkgs";
392$cmd = "$pbos->{'localinstall'} $pkgs" if ((defined $local) && (defined $pbos->{'localinstall'}) && ($pbos->{'localinstall'} !~ /[ ]*/));
393my $ret = pb_system($cmd, "Installing packages ($cmd)","mayfail");
394# Try to accomodate deficient proxies
395if ($ret != 0) {
396 $ret = pb_system($cmd, "Re-trying installing packages ($cmd)");
397}
398confess "Some packages did not install" if (($ret != 0) && ($Global::pb_stop_on_error));
399}
400
401
402=item B<pb_distro_installdeps>
403
404This function install the dependencies required to build the package on a distro.
405If $forcerepo is defined then do not assume packages are alredy installed, but reinstall them
406(useful if you add a repo which contains more up to date packages that you need)
407Dependencies can be passed as the 4th parameter in which case they are not computed
408
409=cut
410
411sub pb_distro_installdeps {
412
413# SPEC file
414my $f = shift;
415my $pbos = shift;
416my $forcerepo = shift;
417my $deps = shift; # optional list of deps to install
418my $local = shift; # optional should we install local packages or remote (for deb command is different)
419my $deps2;
420
421if (not defined $deps) {
422 $deps = pb_distro_getdeps($f,$pbos, $forcerepo);
423}
424pb_log(2,"DEBUG: Packages to install: $deps\n") if (defined $deps);
425return if ((not defined $deps) || ($deps =~ /^\s*$/));
426
427pb_distro_installpkgs($pbos,$deps,$local);
428# Check that all deps have been installed correctly
429# This time we don't forcerepo to avoid getting a list as a return as we have
430# already forced it previously, and this time we just want to check
431$deps = pb_distro_getdeps($f, $pbos, undef);
432confess "Some dependencies did not install ($deps)" if ((defined $deps) && ($deps =~ /\S/) && ($Global::pb_stop_on_error));
433}
434
435=item B<pb_distro_getdeps>
436
437This function computes the dependencies indicated in the build file and return them as a string of packages to install
438
439=cut
440
441sub pb_distro_getdeps {
442
443my $f = shift;
444my $pbos = shift;
445my $forcerepo = shift;
446
447my $regexp = "";
448my $deps = "";
449my $sep = $/;
450
451# Protection
452return("") if (not defined $pbos->{'type'});
453return("") if (not defined $f);
454
455pb_log(3,"entering pb_distro_getdeps: $pbos->{'type'} - $f\n");
456if ($pbos->{'type'} eq "rpm") {
457 # In RPM this could include files, but we do not handle them atm.
458 $regexp = '^BuildRequires:(.*)$';
459} elsif ($pbos->{'type'} eq "deb") {
460 $regexp = '^Build-Depends:(.*)$';
461} elsif ($pbos->{'type'} eq "aur") {
462 $regexp = '^makedepends=(.*)$';
463} elsif ($pbos->{'type'} eq "apk") {
464 $regexp = '^makedepends=(.*)$';
465} elsif ($pbos->{'type'} eq "ebuild") {
466 $sep = '"'.$/;
467 $regexp = '^DEPEND="(.*)"\n';
468} else {
469 # No idea
470 return("");
471}
472pb_log(2,"regexp: $regexp\n");
473
474# Preserve separator before using the one we need
475my $oldsep = $/;
476$/ = $sep;
477open(DESC,"$f") || (cluck "Unable to open $f" && return(""));
478while (<DESC>) {
479 pb_log(4,"read: $_\n");
480 next if (! /$regexp/);
481 chomp();
482
483 my $nextline;
484 # Support multi-lines deps for .deb
485 if ($pbos->{type} eq 'deb') {
486 while ($nextline = <DESC>) {
487 last unless $nextline =~ /^\s+(.+)$/o;
488 $_ .= $1;
489 }
490 }
491
492 # What we found with the regexp is the list of deps.
493 pb_log(2,"found deps: $_\n");
494 s/$regexp/$1/i;
495 pb_log(4,"found deps 1: $_\n");
496 # Remove conditions in the middle and at the end for deb
497 s/\([><=]+[^,]*,/,/g;
498 pb_log(4,"found deps 2: $_\n");
499 s/\([><=]+[^,]*$//g;
500 pb_log(4,"found deps 3: $_\n");
501 # Same for rpm
502 s/[><=]+[^,]*,/,/g;
503 pb_log(4,"found deps 4: $_\n");
504 s/[><=]+.*$//g;
505 pb_log(4,"found deps 5: $_\n");
506 # Improve string format (remove , and spaces at start, end and in double
507 s/,/ /g;
508 pb_log(4,"found deps 6: $_\n");
509 s/^\s*//;
510 pb_log(4,"found deps 7: $_\n");
511 # $ here removes the \n
512 s/\s*$//;
513 pb_log(4,"found deps 8: $_\n");
514 s/\s+/ /g;
515 pb_log(4,"found deps 9: $_\n");
516 $deps .= " ".$_;
517 pb_log(4,"found deps end: $deps\n");
518
519 # Support multi-lines deps for .deb (fwup)
520 if (defined $nextline) {
521 $_ = $nextline;
522 redo;
523 }
524}
525close(DESC);
526$/ = $oldsep;
527pb_log(2,"now deps: $deps\n");
528if (defined $forcerepo) {
529 # We want to force installation of all pkgs
530 # because a repo was setup in between, which may contains updated versions
531 pb_log(0,"Forcing installation of all packages due to previous repo setup\n");
532 return($deps);
533} else {
534 pb_log(0,"Installation of only necessary packages\n");
535 my $deps2 = pb_distro_only_deps_needed($pbos,$deps);
536 return($deps2);
537}
538}
539
540
541=item B<pb_distro_only_deps_needed>
542
543This function returns only the dependencies not yet installed
544
545=cut
546
547sub pb_distro_only_deps_needed {
548
549my $pbos = shift;
550my $deps = shift;
551
552return("") if ((not defined $deps) || ($deps =~ /^\s*$/));
553my $deps2 = "";
554# Avoid to install what is already there
555delete $ENV{'COLUMNS'};
556foreach my $p (split(/\s+/,$deps)) {
557 next if $p =~ /^\s*$/o;
558 if ($pbos->{'type'} eq "rpm") {
559 my $rpmcmd = "rpm -q --whatprovides --quiet";
560 # whatprovides doesn't work for RH6.2
561 $rpmcmd = "rpm -q --quiet" if (($pbos->{'name'} eq "redhat") && ($pbos->{'version'} =~ /6/));
562 my $res = pb_system("$rpmcmd '".$p."'","Looking for $p","mayfail");
563 next if ($res eq 0);
564 pb_log(1, "INFO: missing dependency $p\n");
565 } elsif ($pbos->{'type'} eq "deb") {
566 my $res = pb_system("dpkg -L $p","Looking for $p","mayfail");
567 next if ($res eq 0);
568 open(CMD,"dpkg -l $p |") || (cluck "Unable to run dpkg -l $p: $!" && next);
569 my $ok = 0;
570 while (<CMD>) {
571 $ok = 1 if /^ii\s+$p/;
572 }
573 close(CMD);
574 next if $ok;
575 pb_log(1, "INFO: missing dependency $p\n");
576 } elsif ($pbos->{'type'} eq "ebuild") {
577 } elsif ($pbos->{'type'} eq "apk") {
578 my $res = pb_system("apk -e info $p","Looking for $p","mayfail");
579 next if ($res eq 0);
580 pb_log(1, "INFO: missing dependency $p\n");
581 } else {
582 # Not reached
583 }
584 $deps2 .= " $p";
585}
586
587$deps2 =~ s/^\s*//;
588pb_log(2,"List of missing packages: $deps2\n");
589return($deps2);
590}
591
592# Internal
593sub pb_distro_compare_repo {
594
595my $src = shift;
596my $dest = shift;
597
598if (not -f $dest) {
599 pb_log(1, "INFO: Creating new file $dest\n");
600} elsif (-f $dest && -s $dest == 0) {
601 pb_log(1, "INFO: Overwriting empty file $dest\n");
602} elsif (-f $dest && compare("$src", $dest) == 0) {
603 pb_log(1, "INFO: Overwriting identical file $dest\n");
604} else {
605 pb_log(0, "ERROR: destination file $dest exists and is different than source $src\n");
606 pb_system("cat $dest","INFO: Dest...\n","verbose");
607 pb_system("cat $src","INFO: New...\n","verbose");
608 pb_log(1, "INFO: Returning...\n");
609 return(1);
610}
611return(0);
612}
613
614
615=item B<pb_distro_setuposrepo>
616
617This function sets up potential additional repository for the setup phase
618
619=cut
620
621sub pb_distro_setuposrepo {
622
623my $pbos = shift;
624
625pb_log(3, "INFO: Adding osrepo from config file\n");
626my %h;
627my $h = \%h;
628# 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
629$h = pb_conf_cache(pb_distro_conffile(),$h);
630#my $repo = pb_distro_get_param($pbos,$osrepo);
631my $repo = pb_distro_get_in_hash_if($pbos,$h,"osrepo");
632return(pb_distro_setuprepo_gen($pbos,$repo));
633}
634
635=item B<pb_distro_setuprepo>
636
637This function sets up potential additional repository to the build/install/test environment
638If done, it returns forcerepo if a repo was added, if not undef
639
640=cut
641
642sub pb_distro_setuprepo {
643
644my $pbos = shift;
645my $repotype = shift;
646
647my %h;
648my $h = \%h;
649# 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
650$h = pb_conf_cache("$ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.yml",$h) if ((defined $ENV{'PBROOTDIR'}) && (-f "$ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.yml"));
651$h = pb_conf_cache("$ENV{'PBDESTDIR'}/pbrc.yml",$h) if ((defined $ENV{'PBDESTDIR'}) && (-f "$ENV{'PBDESTDIR'}/pbrc.yml"));
652
653my $repo = pb_distro_get_in_hash_if($pbos,$h,"add".$repotype."repo");
654# If no repo then set it up as undef so the return value is correct from pb_distro_setuprepo_gen
655$repo = undef if ((defined $repo) && ($repo eq ""));
656if ($repotype =~ /install/) {
657 # Give a probable default if nothing is provided to avoid overloading conf files
658 #
659 if (not defined $repo) {
660 my ($pbrepo) = pb_conf_get_in_hash_if($h,"pbrepo");
661 if (not defined $pbrepo) {
662 cluck("No pbrepo parameter defined in your project configuration file, please fix !");
663 return(undef);
664 }
665 my $url = "$pbrepo->{$ENV{'PBPROJ'}}";
666 my ($testver,$delivery) = pb_conf_get_in_hash_if($h,"testver","delivery");
667 $delivery->{$ENV{'PBPROJ'}} = "" if (not defined $delivery->{$ENV{'PBPROJ'}});
668 $url .= "/$delivery->{$ENV{'PBPROJ'}}/";
669 my $repotag = "";
670 $repotag = "-$delivery->{$ENV{'PBPROJ'}}" if ($delivery->{$ENV{'PBPROJ'}} ne "");
671 $url .= "/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/$ENV{PBPROJ}$repotag.";
672 my $ext = "";
673 if ($pbos->{'type'} eq "rpm") {
674 $ext = "repo";
675 if ($pbos->{'family'} eq "md") {
676 $ext = "addmedia";
677 }
678 } elsif ($pbos->{'type'} eq "deb") {
679 $ext = ".sources.list";
680 }
681 $repo = $url.$ext;
682 }
683}
684pb_log(1, "INFO: Adding $repo from config file for $repotype step\n") if (defined $repo);
685return(pb_distro_setuprepo_gen($pbos,$repo));
686}
687
688=item B<pb_distro_setuprepo_gen>
689
690This functionthe sets up in a generic way potential additional repository passed as a param
691It returns forcerepo if one was added, else undef
692
693=cut
694
695sub pb_distro_setuprepo_gen {
696
697my $pbos = shift;
698my $param = shift;
699
700return(undef) if (not defined $param);
701
702pb_apply_conf_proxy($pbos);
703
704# Loop on the list of additional repo
705foreach my $i (split(/,/,$param)) {
706
707 pb_log(1,"Adding repository defined by $i\n");
708 my ($scheme, $account, $host, $port, $path) = pb_get_uri($i);
709 my $bn = basename($i);
710
711 # The repo file can be local or remote. download or copy at the right place
712 if (($scheme eq "ftp") || ($scheme =~ /http/)) {
713 pb_system("wget -O $ENV{'PBTMP'}/$bn $i","Downloading additional repository file $i");
714 } else {
715 copy($i,"$ENV{'PBTMP'}/$bn");
716 }
717
718 # The repo file can be a real file or a package
719 if ($pbos->{'type'} eq "rpm") {
720 if ($bn =~ /\.rpm$/) {
721 my $pn = $bn;
722 $pn =~ s/\.rpm//;
723 if (pb_system("rpm -q --quiet $pn","","mayfail") != 0) {
724 pb_system("sudo rpm -Uvh $ENV{'PBTMP'}/$bn","Adding package $bn to setup repository");
725 }
726 } elsif ($bn =~ /\.repo$/) {
727 my $dirdest = "";
728 my $reponame = "";
729 # TODO: could go in pb.yml in fact
730 if ($pbos->{install} =~ /\byum\b/) {
731 $reponame="yum";
732 $dirdest = "/etc/yum.repos.d";
733 } elsif ($pbos->{install} =~ /\bdnf\b/) {
734 $reponame="dnf";
735 $dirdest = "/etc/yum.repos.d";
736 } elsif ($pbos->{install} =~ /\bzypper\b/) {
737 $reponame="zypper";
738 $dirdest = "/etc/zypp/repos.d";
739 } else {
740 cluck "Unknown location for repository file for '$pbos->{install}' command";
741 next;
742 }
743 my $dest = "$dirdest/$bn";
744 return(undef) if (pb_distro_compare_repo("$ENV{'PBTMP'}/$bn",$dest) == 1);
745 if (! -d $dirdest) {
746 cluck "Missing directory $dirdest ($reponame)";
747 return(undef);
748 }
749 pb_system("sudo mv $ENV{'PBTMP'}/$bn $dest","Adding $reponame repository") if (not -f "$dest");
750 # OpenSUSE does't seem to import keys automatically
751 # :-(
752 if ($pbos->{install} =~ /\bzypper\b/) {
753 my $keyfile = undef;
754 open(REPO,"$dest") || (cluck "Unable to open $dest" && next);
755 while (<REPO>) {
756 $keyfile = $_;
757 if ($keyfile =~ /^gpgkey=/) {
758 $keyfile =~ s/gpgkey=//;
759 last;
760 }
761 }
762 close(REPO);
763 if (defined $keyfile) {
764 pb_system("wget -O $ENV{'PBTMP'}/$bn $keyfile","Downloading GPG key file $keyfile");
765 pb_system("sudo rpm --import $ENV{'PBTMP'}/$bn","Importing GPG key file $keyfile");
766 unlink("$ENV{'PBTMP'}/$bn");
767 }
768 }
769 } elsif ($bn =~ /\.addmedia/) {
770 # URPMI repo
771 # We should test that it's not an already setup urpmi repo
772 open(URPMI,"/etc/urpmi/urpmi.cfg") || (cluck "Unable to open /etc/urpmi/urpmi.cfg" && next);
773 my $found = 0;
774 my $entry = $bn;
775 $entry =~ s/.addmedia$//;
776 while (<URPMI>) {
777 $found = 1 if ($_ =~ /^$entry /);
778 }
779 pb_system("chmod 755 $ENV{'PBTMP'}/$bn ; sudo $ENV{'PBTMP'}/$bn 2>&1 > /dev/null","Adding urpmi repository") if ($found == 0);
780 pb_log(0,"INFO urpmi $bn already set up\n") if ($found == 1);
781 } else {
782 pb_log(0,"ERROR: Unable to deal with repository file $i on rpm distro ! Please report to dev team\n");
783 }
784 } elsif ($pbos->{'type'} eq "deb") {
785 if ($bn =~ /\.sources.list$/) {
786 my $dest = "/etc/apt/sources.list.d/$bn";
787 return(undef) if (pb_distro_compare_repo("$ENV{'PBTMP'}/$bn",$dest) == 1);
788 pb_system("sudo mv $ENV{'PBTMP'}/$bn $dest","Adding apt repository $dest");
789 # Check whether GPG keys for this repo are already known and if
790 # not add them
791 open(REPO,"$dest") || (cluck "Unable to open $dest" && next);
792 my $debrepo;
793 while (<REPO>) {
794 if (/^deb\s/) {
795 $debrepo = $_;
796 chomp($debrepo);
797 $debrepo =~ s|^deb ([^\s]+)\s([^\s]+)\s([^\s]+)|$1/dists/$2|;
798 last;
799 }
800 }
801 close(REPO);
802 return(undef) if (not defined $debrepo);
803
804 pb_system("wget -O $ENV{'PBTMP'}/Release $debrepo/Release","Downloading $debrepo/Release");
805 pb_system("wget -O $ENV{'PBTMP'}/Release.gpg $debrepo/Release.gpg","Downloading $debrepo/Release.gpg");
806 my $signature = undef;
807 my ($pbgpgserver) = pb_conf_get("pbgpgserver");
808 confess "Unable to find a GPG server in configuration, please define pbgpgserver" if (not defined $pbgpgserver);
809 my $keyserver = $pbgpgserver->{$ENV{'PBPROJ'}};
810 $keyserver = $pbgpgserver->{'default'} if (not defined $keyserver);
811 confess "Unable to find a GPG server in configuration, please define correctly pbgpgserver" if (not defined $keyserver);
812 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);
813 while(<SIGN>) {
814 chomp();
815 if (/^gpg: .*key /) {
816 $signature = $_;
817 $signature =~ s/^gpg: .*key [ID ]*([A-Z0-9]+)/$1/;
818 pb_system("gpg --recv-keys --keyserver $keyserver $signature","Importing GPG signature for $signature");
819 last;
820 }
821 }
822 close(SIGN);
823 return(undef) if (not defined $signature);
824
825 pb_log(3, "GnuPG repo verify returned: $signature\n");
826 pb_system("gpg -a --export -o $ENV{'PBTMP'}/apt.sig \'$signature\'","Exporting GnuPG signature of $signature");
827 pb_system("sudo apt-key add $ENV{'PBTMP'}/apt.sig","Adding GnuPG signature of $signature to APT key ring");
828 pb_system("sudo apt-get update","Updating apt repository");
829 } else {
830 pb_log(0,"ERROR: Unable to deal with repository file $i on deb distro ! Please report to dev team\n");
831 }
832 } else {
833 pb_log(0,"ERROR: Unable to deal with repository file $i on that distro ! Please report to dev team\n");
834 }
835}
836return("forcerepo");
837}
838
839=item B<pb_distro_to_keylist>
840
841Given a pbos object (first param) and the generic key (second param), get the list of possible keys for looking up variable for
842filter names. The list will be sorted most-specific to least specific.
843
844=cut
845
846sub pb_distro_to_keylist ($$) {
847
848my ($pbos, $generic) = @_;
849
850foreach my $key (qw/name version arch family type os/) {
851 confess "missing pbos key $key" unless (defined $pbos->{$key});
852}
853
854my @keylist = ("$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}", "$pbos->{'name'}-$pbos->{'version'}");
855
856# Loop to include also previous minor versions
857# if configured so
858if ((defined $pbos->{'useminor'}) && ($pbos->{'useminor'} eq "true") && ($pbos->{'version'} =~ /^(\d+)\.(\d+)$/o)) {
859 my ($major, $minor) = ($1, $2);
860 while ($minor > 0) {
861 $minor--;
862 push (@keylist, "$pbos->{'name'}-${major}.$minor");
863 }
864 push (@keylist, "$pbos->{'name'}-$major");
865}
866
867push (@keylist, $pbos->{'name'}, $pbos->{'family'}, $pbos->{'type'}, $pbos->{'os'}, $generic);
868return @keylist;
869}
870
871=item B<pb_distro_get_param>
872
873This internal function gets the parameters in the conf file from the most precise tuple up to default
874
875=cut
876
877sub pb_distro_get_param {
878
879my $pbos = shift;
880my $var = shift;
881my @param = ();
882my $i = 0;
883pb_log(3,"var: ".Dumper($var));
884
885my @keylist = pb_distro_to_keylist($pbos,"default");
886pb_log(3,"keylist: ".Dumper(@keylist));
887
888my $p = $var->{"ptr"};
889foreach my $opt (@$p) {
890 pb_log(3,'opt: '.Dumper($opt));
891 my $param = undef;
892 my $fkey = undef;
893 foreach my $key (@keylist) {
894 pb_log(3,"key: $key\n");
895 if (defined $opt->{$key}) {
896 $param = $opt->{$key};
897 $fkey = $key;
898 last;
899 }
900 }
901 my $field = $var->{"val"};
902 pb_log(3,"field: ".Dumper($field));
903 $fkey = "default" if (not defined $fkey);
904 pb_log(3,"key: $fkey\n");
905 pb_log(2,"DEBUG: pb_distro_get_if on $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'} for $field->[$i]".'{'.$fkey.'}'." returns ");
906 if (defined $param) {
907 # Allow replacement of variables inside the parameter such as name, version, arch for rpmbootstrap
908 # but not shell variable which are backslashed
909 if ($param =~ /[^\\]\$/) {
910 pb_log(3,"Expanding variable on $param\n");
911 eval { $param =~ s/(\$\w+->\{\'\w+\'\})/$1/eeg };
912 }
913 pb_log(2,"$param\n");
914 } else {
915 pb_log(2,"undefined\n");
916 }
917 push @param,$param;
918 $i++;
919}
920
921# Return one param if user only asked for one lookup, an array if not.
922my $nb = @param;
923pb_log(3,"Param".Dumper(@param)." has $nb members\n");
924if ($nb eq 1) {
925 pb_log(3,"Return param $param[0]\n") if (defined $param[0]);
926 return($param[0]);
927} else {
928 return(@param);
929}
930}
931
932
933=item B<pb_distro_get_if>
934
935This function gets the parameters in the conf file from the most precise tuple up to default
936
937=cut
938
939sub pb_distro_get_if {
940
941my $pbos = shift;
942my @ptr = pb_conf_get_if(@_);
943my $var;
944$var->{"ptr"} = \@ptr;
945$var->{"val"} = \@_;
946return(pb_distro_get_param($pbos,$var));
947}
948
949=item B<pb_distro_get>
950
951This function gets the parameters in the conf file from the most precise tuple up to default.
952Aborts of one param doesn't exist whereas it should
953
954=cut
955
956sub pb_distro_get {
957
958my $pbos = shift;
959my @param = @_;
960my @return = pb_distro_get_if($pbos,@param);
961
962foreach my $i (0..$#param) {
963 confess "No $param[$i] defined for $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}" if (not defined $return[$i]);
964}
965my $nb = @return;
966if ($nb eq 1) {
967 return($return[0]);
968} else {
969 return(@return);
970}
971}
972
973=item B<pb_distro_get_in_hash_if>
974
975This function gets the parameters in the conf file passed as hash from the most precise tuple up to default
976
977=cut
978
979sub pb_distro_get_in_hash_if {
980
981my $pbos = shift;
982my $lh = shift || return(());
983
984my @ptr = pb_conf_get_in_hash_if($lh,@_);
985my $var;
986$var->{"ptr"} = \@ptr;
987$var->{"val"} = \@_;
988return(pb_distro_get_param($pbos,$var));
989}
990
991=item B<pb_distro_get_context>
992
993This function gets the OS context passed as parameter and return the corresponding distribution hash
994If passed undef or "" then auto-detects
995
996=cut
997
998
999sub pb_distro_get_context {
1000
1001my $os = shift;
1002my $pbos;
1003
1004if ((defined $os) && ($os ne "")) {
1005 my ($name,$ver,$darch) = split(/-/,$os);
1006 pb_log(0,"Bad format for $os") if ((not defined $name) || (not defined $ver) || (not defined $darch)) ;
1007 chomp($darch);
1008 $pbos = pb_distro_init($name,$ver,$darch);
1009} else {
1010 $pbos = pb_distro_init();
1011}
1012return($pbos);
1013}
1014
1015=item B<pb_distro_conf_print>
1016
1017This 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.
1018
1019=cut
1020
1021sub pb_distro_conf_print {
1022
1023my $pbos = shift;
1024my @keys = @_;
1025my $all = 0;
1026
1027if ($#keys == -1) {
1028 pb_log(0,"Full pb configuration for project $ENV{'PBPROJ'}\n");
1029 pb_log(0,"================================================\n");
1030 @keys = pb_conf_get_all();
1031 $all = 1;
1032}
1033if (defined $ENV{'PBV'}) {
1034 pb_log(1,"Distribution $ENV{'PBV'}\n");
1035 pb_log(1,"========================\n");
1036} else {
1037 pb_log(1,"Local Distribution\n");
1038 pb_log(1,"==================\n");
1039}
1040
1041my %rep;
1042my $i = 0;
1043# Index on distro
1044foreach my $r (pb_distro_get_if($pbos,@keys)) {
1045 $rep{$keys[$i]} = $r if (defined $keys[$i]);
1046 $i++;
1047}
1048$i = 0;
1049# Then Index on prj to overwrite previous value if needed
1050foreach my $r (pb_conf_get_if(@keys)) {
1051 $rep{$keys[$i]} = $r->{'default'} if (defined $r->{'default'});
1052 $rep{$keys[$i]} = $r->{$ENV{'PBPROJ'}} if (defined $r->{$ENV{'PBPROJ'}});
1053 $i++;
1054}
1055foreach my $r (keys %rep) {
1056 pb_log(1, "$r => ");
1057 pb_log(0, "$rep{$r}\n") if (($all == 0) && (defined $rep{$r}));
1058 pb_log(0, "$r = $rep{$r}\n") if (($all == 1) && (defined $rep{$r}));
1059}
1060}
1061
1062
1063=back
1064
1065=head1 WEB SITES
1066
1067The 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/>.
1068
1069=head1 USER MAILING LIST
1070
1071None exists for the moment.
1072
1073=head1 AUTHORS
1074
1075The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
1076
1077=head1 COPYRIGHT
1078
1079Project-Builder.org is distributed under the GPL v2.0 license
1080described in the file C<COPYING> included with the distribution.
1081
1082=cut
1083
1084
10851;
Note: See TracBrowser for help on using the repository browser.