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

Revision 1181, 17.8 KB checked in by bruno, 2 years ago (diff)
  • Fix rpmbootstrap and pb newve order, which wasn't working after the latest pbos introduction
  • rpmbootstrap wasn't using the right conf file when launched with sudo is fixed by using the SUDO_USER env. var.
  • pb.conf rbsmirrorsrv now uses pbos var, and is filtered correctly.
Line 
1#!/usr/bin/perl -w
2#
3# Creates common environment for distributions
4#
5# $Id$
6#
7
8package ProjectBuilder::Distribution;
9
10use strict;
11use Data::Dumper;
12use ProjectBuilder::Version;
13use ProjectBuilder::Base;
14use ProjectBuilder::Conf;
15use File::Basename;
16use File::Copy;
17
18# Global vars
19# Inherit from the "Exporter" module which handles exporting functions.
20 
21use vars qw($VERSION $REVISION @ISA @EXPORT);
22use Exporter;
23 
24# Export, by default, all the functions into the namespace of
25# any code which uses this module.
26 
27our @ISA = qw(Exporter);
28our @EXPORT = qw(pb_distro_conffile pb_distro_get pb_distro_getlsb pb_distro_installdeps pb_distro_getdeps pb_distro_only_deps_needed pb_distro_setuprepo pb_distro_setuposrepo pb_distro_get_param pb_distro_get_context);
29($VERSION,$REVISION) = pb_version_init();
30
31=pod
32
33=head1 NAME
34
35ProjectBuilder::Distribution, part of the project-builder.org - module dealing with distribution detection
36
37=head1 DESCRIPTION
38
39This modules provides functions to allow detection of Linux distributions, and giving back some attributes concerning them.
40
41=head1 SYNOPSIS
42
43  use ProjectBuilder::Distribution;
44
45  #
46  # Return information on the running distro
47  #
48  my $pbos = pb_distro_get_context();
49  print "distro tuple: ".Dumper($pbos->name, $pbos->ver, $pbos->fam, $pbos->type, $pbos->pbsuf, $pbos->pbupd, $pbos->pbins, $pbos->arch)."\n";
50  #
51  # Return information on the requested distro
52  #
53  my $pbos = pb_distro_get_context("ubuntu-7.10-x86_64");
54  print "distro tuple: ".Dumper($pbos->name, $pbos->ver, $pbos->fam, $pbos->type, $pbos->pbsuf, $pbos->pbupd, $pbos->pbins, $pbos->arch)."\n";
55  #
56  # Return information on the running distro
57  #
58  my ($ddir,$dver) = pb_distro_get();
59
60=head1 USAGE
61
62=over 4
63
64=item B<pb_distro_conffile>
65
66This function returns the mandatory configuration file used for distribution/OS detection
67
68=cut
69
70sub pb_distro_conffile {
71
72return("CCCC/pb.conf");
73}
74
75
76=item B<pb_distro_init>
77
78This 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.
79
80As an example, Ubuntu and Debian are in the same "du" family. As well as RedHat, RHEL, CentOS, fedora are on the same "rh" family.
81Mandriva, Open SuSE and Fedora have all the same "rpm" type of build system. Ubuntu and Debian have the same "deb" type of build system.
82And "fc" is the extension generated for all Fedora packages (Version will be added by pb).
83All this information is stored in an external configuration file typically at /etc/pb/pb.conf
84
85When 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.
86
87Cf: http://linuxmafia.com/faq/Admin/release-files.html
88Ideas taken from http://search.cpan.org/~kerberus/Linux-Distribution-0.14/lib/Linux/Distribution.pm
89
90=cut
91
92
93sub pb_distro_init {
94
95my $pbos = {
96        'name' => undef,
97        'version' => undef,
98        'arch' => undef,
99        'family' => "unknown",
100        'suffix' => "unknown",
101        'update' => "unknown",
102        'install' => "unknown",
103        'type' => "unknown",
104        'os' => "unknown",
105        'nover' => "false",
106        'rmdot' => "false",
107        };
108$pbos->{'name'} = shift;
109$pbos->{'version'} = shift;
110$pbos->{'arch'} = shift;
111
112# Adds conf file for distribution description
113# the location of the conf file is finalyzed at install time
114# depending whether we deal with package install or tar file install
115pb_conf_add(pb_distro_conffile());
116
117# If we don't know which distribution we're on, then guess it
118($pbos->{'name'},$pbos->{'version'}) = pb_distro_get() if ((not defined $pbos->{'name'}) || (not defined $pbos->{'version'}));
119
120# For some rare cases, typically nover ones
121$pbos->{'name'} = "unknown" if (not defined $pbos->{'name'});
122$pbos->{'version'} = "unknown" if (not defined $pbos->{'version'});
123
124# Initialize arch
125$pbos->{'arch'} = pb_get_arch() if (not defined $pbos->{'arch'});
126
127# Dig into the tuple to find the best answer
128# Do NOT factorize here, as it won't work as of now for hash creation
129$pbos->{'family'} = pb_distro_get_param($pbos,pb_conf_get("osfamily"));
130$pbos->{'type'} = pb_distro_get_param($pbos,pb_conf_get("ostype"));
131($pbos->{'os'},$pbos->{'install'},$pbos->{'suffix'},$pbos->{'nover'},$pbos->{'rmdot'},$pbos->{'update'}) = pb_distro_get_param($pbos,pb_conf_get("os","osins","ossuffix","osnover","osremovedotinver","osupd"));
132#($pbos->{'family'},$pbos->{'type'},$pbos->{'os'},$pbos->{'install'},$pbos->{'suffix'},$pbos->{'nover'},$pbos->{'rmdot'},$pbos->{'update'}) = pb_distro_get_param($pbos,pb_conf_get("osfamily","ostype","os","osins","ossuffix","osnover","osremovedotinver","osupd"));
133
134# Some OS have no interesting version
135$pbos->{'version'} = "nover" if ((defined $pbos->{'nover'}) && ($pbos->{'nover'} eq "true"));
136
137# For some OS remove the . in version name for extension
138my $dver2 = $pbos->{'version'};
139$dver2 =~ s/\.//g if ((defined $pbos->{'rmdot'}) && ($pbos->{'rmdot'} eq "true"));
140
141if ((not defined $pbos->{'suffix'}) || ($pbos->{'suffix'} eq "")) {
142        # By default suffix is a concatenation of name and version
143        $pbos->{'suffix'} = ".$pbos->{'name'}$dver2" 
144} else {
145        # concat just the version to what has been found
146        $pbos->{'suffix'} = ".$pbos->{'suffix'}$dver2";
147}
148
149#       if ($arch eq "x86_64") {
150#       $opt="--exclude=*.i?86";
151#       }
152pb_log(2,"DEBUG: pb_distro_init: ".Dumper($pbos)."\n");
153
154return($pbos);
155}
156
157=item B<pb_distro_get>
158
159This 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.
160
161On my home machine it would currently report ("mandriva","2010.2").
162
163=cut
164
165sub pb_distro_get {
166
167# 1: List of files that unambiguously indicates what distro we have
168# 2: List of files that ambiguously indicates what distro we have
169# 3: Should have the same keys as the previous one. If ambiguity, which other distributions should be checked
170# 4: Matching Rg. Expr to detect distribution and version
171my ($single_rel_files, $ambiguous_rel_files,$distro_similar,$distro_match) = pb_conf_get("osrelfile","osrelambfile","osambiguous","osrelexpr");
172
173my $release;
174my $distro;
175
176# Begin to test presence of non-ambiguous files
177# that way we reduce the choice
178my ($d,$r);
179while (($d,$r) = each %$single_rel_files) {
180        if (defined $ambiguous_rel_files->{$d}) {
181                print STDERR "The key $d is considered as both unambiguous and ambigous.\n";
182                print STDERR "Please fix your configuration file.\n"
183        }
184        if (-f "$r" && ! -l "$r") {
185                my $tmp=pb_get_content("$r");
186                # Found the only possibility.
187                # Try to get version and return
188                if (defined ($distro_match->{$d})) {
189                        ($release) = $tmp =~ m/$distro_match->{$d}/m;
190                } else {
191                        print STDERR "Unable to find $d version in $r (non-ambiguous)\n";
192                        print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
193                        $release = "unknown";
194                }
195                return($d,$release);
196        }
197}
198
199# Now look at ambiguous files
200# Ubuntu before 10.04 includes a /etc/debian_version file that creates an ambiguity with debian
201# So we need to look at distros in reverse alphabetic order to treat ubuntu always first via lsb
202foreach $d (reverse keys %$ambiguous_rel_files) {
203        $r = $ambiguous_rel_files->{$d};
204        if (-f "$r" && !-l "$r") {
205                # Found one possibility.
206                # Get all distros concerned by that file
207                my $tmp=pb_get_content("$r");
208                my $found = 0;
209                my $ptr = $distro_similar->{$d};
210                pb_log(2,"amb: ".Dumper($ptr)."\n");
211                $release = "unknown";
212                foreach my $dd (split(/,/,$ptr)) {
213                        pb_log(2,"check $dd\n");
214                        # Try to check pattern
215                        if (defined $distro_match->{$dd}) {
216                                pb_log(2,"cmp: $distro_match->{$dd} - vs - $tmp\n");
217                                ($release) = $tmp =~ m/$distro_match->{$dd}/m;
218                                if ((defined $release) && ($release ne "unknown")) {
219                                        $distro = $dd;
220                                        $found = 1;
221                                        last;
222                                }
223                        }
224                }
225                if ($found == 0) {
226                        print STDERR "Unable to find $d version in $r (ambiguous)\n";
227                        print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
228                        $release = "unknown";
229                } else {
230                        return($distro,$release);
231                }
232        }
233}
234return("unknown","unknown");
235}
236
237=item B<pb_distro_getlsb>
238
239This function returns the 5 lsb values LSB version, distribution ID, Description, release and codename.
240As entry it takes an optional parameter to specify whether the output is short or not.
241
242=cut
243
244sub pb_distro_getlsb {
245
246my $s = shift;
247pb_log(3,"Entering pb_distro_getlsb\n");
248
249my ($ambiguous_rel_files) = pb_conf_get("osrelambfile");
250my $lsbf = $ambiguous_rel_files->{"lsb"};
251
252# LSB has not been configured.
253if (not defined $lsbf) {
254        print STDERR "no lsb entry defined for osrelambfile\n";
255        die "You modified upstream delivery and lost !\n";
256}
257
258if (-r $lsbf) {
259        my $rep = pb_get_content($lsbf);
260        # Create elementary fields
261        my ($c, $r, $d, $i, $l) = ("", "", "", "", "");
262        for my $f (split(/\n/,$rep)) {
263                pb_log(3,"Reading file part ***$f***\n");
264                $c = $f if ($f =~ /^DISTRIB_CODENAME/);
265                $c =~ s/DISTRIB_CODENAME=/Codename:\t/;
266                $r = $f if ($f =~ /^DISTRIB_RELEASE/);
267                $r =~ s/DISTRIB_RELEASE=/Release:\t/;
268                $d = $f if ($f =~ /^DISTRIB_DESCRIPTION/);
269                $d =~ s/DISTRIB_DESCRIPTION=/Description:\t/;
270                $d =~ s/"//g;
271                $i = $f if ($f =~ /^DISTRIB_ID/);
272                $i =~ s/DISTRIB_ID=/Distributor ID:\t/;
273                $l = $f if ($f =~ /^LSB_VERSION/);
274                $l =~ s/LSB_VERSION=/LSB Version:\t/;
275        }
276        my $regexp = "^[A-z ]*:[\t ]*";
277        $c =~ s/$regexp// if (defined $s);
278        $r =~ s/$regexp// if (defined $s);
279        $d =~ s/$regexp// if (defined $s);
280        $i =~ s/$regexp// if (defined $s);
281        $l =~ s/$regexp// if (defined $s);
282        return($l, $i, $d, $r, $c);
283} else {
284        print STDERR "Unable to read $lsbf file\n";
285        die "Please report to the maintainer bruno_at_project-builder.org\n";
286}
287}
288
289=item B<pb_distro_installdeps>
290
291This function install the dependencies required to build the package on a distro.
292Dependencies can be passed as a parameter in which case they are not computed
293
294=cut
295
296sub pb_distro_installdeps {
297
298# SPEC file
299my $f = shift || undef;
300my $pbos = shift;
301my $deps = shift || undef;
302
303# Protection
304return if (not defined $pbos->{'update'});
305
306# Get dependencies in the build file if not forced
307$deps = pb_distro_getdeps($f, $pbos) if (not defined $deps);
308pb_log(2,"deps: $deps\n");
309return if ((not defined $deps) || ($deps =~ /^\s*$/));
310if ($deps !~ /^[        ]*$/) {
311        # This may not be // proof. We should test for availability of repo and sleep if not
312        pb_system("$pbos->{'update'} $deps","Installing dependencies ($deps)");
313        }
314}
315
316=item B<pb_distro_getdeps>
317
318This function computes the dependencies indicated in the build file and return them as a string of packages to install
319
320=cut
321
322sub pb_distro_getdeps {
323
324my $f = shift || undef;
325my $pbos = shift;
326
327my $regexp = "";
328my $deps = "";
329my $sep = $/;
330
331# Protection
332return("") if (not defined $pbos->{'type'});
333return("") if (not defined $f);
334
335pb_log(3,"entering pb_distro_getdeps: $pbos->{'type'} - $f\n");
336if ($pbos->{'type'} eq  "rpm") {
337        # In RPM this could include files, but we do not handle them atm.
338        $regexp = '^BuildRequires:(.*)$';
339} elsif ($pbos->{'type'} eq "deb") {
340        $regexp = '^Build-Depends:(.*)$';
341} elsif ($pbos->{'type'} eq "ebuild") {
342        $sep = '"'.$/;
343        $regexp = '^DEPEND="(.*)"\n'
344} else {
345        # No idea
346        return("");
347}
348pb_log(2,"regexp: $regexp\n");
349
350# Preserve separator before using the one we need
351my $oldsep = $/;
352$/ = $sep;
353open(DESC,"$f") || die "Unable to open $f";
354while (<DESC>) {
355        pb_log(4,"read: $_\n");
356        next if (! /$regexp/);
357        chomp();
358        # What we found with the regexp is the list of deps.
359        pb_log(2,"found deps: $_\n");
360        s/$regexp/$1/i;
361        # Remove conditions in the middle and at the end for deb
362        s/\(\s*[><=]+.*\)[^,]*,/,/g;
363        s/\(\s*[><=]+.*$//g;
364        # Same for rpm
365        s/[><=]+[^,]*,/,/g;
366        s/[><=]+.*$//g;
367        # Improve string format (remove , and spaces at start, end and in double
368        s/,/ /g;
369        s/^\s*//;
370        s/\s*$//;
371        s/\s+/ /g;
372        $deps .= " ".$_;
373}
374close(DESC);
375$/ = $oldsep;
376pb_log(2,"now deps: $deps\n");
377my $deps2 = pb_distro_only_deps_needed($pbos,$deps);
378return($deps2);
379}
380
381
382=item B<pb_distro_only_deps_needed>
383
384This function returns only the dependencies not yet installed
385
386=cut
387
388sub pb_distro_only_deps_needed {
389
390my $pbos = shift;
391my $deps = shift || undef;
392
393return("") if ((not defined $deps) || ($deps =~ /^\s*$/));
394my $deps2 = "";
395# Avoid to install what is already there
396foreach my $p (split(/ /,$deps)) {
397        if ($pbos->{'type'} eq  "rpm") {
398                my $res = pb_system("rpm -q --whatprovides --quiet $p","","quiet");
399                next if ($res eq 0);
400        } elsif ($pbos->{'type'} eq "deb") {
401                my $res = pb_system("dpkg -L $p","","quiet");
402                next if ($res eq 0);
403        } elsif ($pbos->{'type'} eq "ebuild") {
404        } else {
405                # Not reached
406        }
407        pb_log(2,"found deps2: $p\n");
408        $deps2 .= " $p";
409}
410
411$deps2 =~ s/^\s*//;
412pb_log(2,"now deps2: $deps2\n");
413return($deps2);
414}
415
416=item B<pb_distro_setuposrepo>
417
418This function sets up potential additional repository for the setup phase
419
420=cut
421
422sub pb_distro_setuposrepo {
423
424my $pbos = shift;
425
426pb_distro_setuprepo_gen($pbos,pb_distro_conffile(),"osrepo");
427}
428
429=item B<pb_distro_setuprepo>
430
431This function sets up potential additional repository to the build environment
432
433=cut
434
435sub pb_distro_setuprepo {
436
437my $pbos = shift;
438
439pb_distro_setuprepo_gen($pbos,"$ENV{'PBDESTDIR'}/pbrc","addrepo");
440}
441
442=item B<pb_distro_setuprepo_gen>
443
444This function sets up in a generic way potential additional repository
445
446=cut
447
448sub pb_distro_setuprepo_gen {
449
450my $pbos = shift;
451my $pbconf = shift || undef;
452my $pbkey = shift || undef;
453
454return if (not defined $pbconf);
455return if (not defined $pbkey);
456my ($addrepo) = pb_conf_read($pbconf,$pbkey);
457return if (not defined $addrepo);
458
459my $param = pb_distro_get_param($pbos,$addrepo);
460return if ($param eq "");
461
462# Loop on the list of additional repo
463foreach my $i (split(/,/,$param)) {
464
465        my ($scheme, $account, $host, $port, $path) = pb_get_uri($i);
466        my $bn = basename($i);
467
468        # The repo file can be local or remote. download or copy at the right place
469        if (($scheme eq "ftp") || ($scheme eq "http")) {
470                pb_system("wget -O $ENV{'PBTMP'}/$bn $i","Donwloading additional repository file $i");
471        } else {
472                copy($i,$ENV{'PBTMP'}/$bn);
473        }
474
475        # The repo file can be a real file or a package
476        if ($pbos->{'type'} eq "rpm") {
477                if ($bn =~ /\.rpm$/) {
478                        my $pn = $bn;
479                        $pn =~ s/\.rpm//;
480                        if (pb_system("rpm -q --quiet $pn","","quiet") != 0) {
481                                pb_system("sudo rpm -Uvh $ENV{'PBTMP'}/$bn","Adding package to setup repository");
482                        }
483                } elsif ($bn =~ /\.repo$/) {
484                        # Yum repo
485                        pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/yum.repos.d","Adding yum repository") if (not -f "/etc/yum.repos.d/$bn");
486                } elsif ($bn =~ /\.addmedia/) {
487                        # URPMI repo
488                        # We should test that it's not already a urpmi repo
489                        pb_system("chmod 755 $ENV{'PBTMP'}/$bn ; sudo $ENV{'PBTMP'}/$bn 2>&1 > /dev/null","Adding urpmi repository");
490                } else {
491                        pb_log(0,"Unable to deal with repository file $i on rpm distro ! Please report to dev team\n");
492                }
493        } elsif ($pbos->{'type'} eq "deb") {
494                if (($bn =~ /\.sources.list$/) && (not -f "/etc/apt/sources.list.d/$bn")) {
495                        pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/apt/sources.list.d","Adding apt repository");
496                        pb_system("sudo apt-get update","Updating apt repository");
497                } else {
498                        pb_log(0,"Unable to deal with repository file $i on deb distro ! Please report to dev team\n");
499                }
500        } else {
501                pb_log(0,"Unable to deal with repository file $i on that distro ! Please report to dev team\n");
502        }
503}
504return;
505}
506
507=item B<pb_distro_get_param>
508
509This function gets the parameter in the conf file from the most precise tuple up to default
510
511=cut
512
513sub pb_distro_get_param {
514
515my @param;
516my $param;
517my $pbos = shift;
518
519pb_log(2,"DEBUG: pb_distro_get_param on $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'} for ".Dumper(@_)."\n");
520foreach my $opt (@_) {
521        if (defined $opt->{"$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}"}) {
522                $param = $opt->{"$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}"};
523        } elsif (defined $opt->{"$pbos->{'name'}-$pbos->{'version'}"}) {
524                $param = $opt->{"$pbos->{'name'}-$pbos->{'version'}"};
525        } elsif (defined $opt->{"$pbos->{'name'}"}) {
526                $param = $opt->{"$pbos->{'name'}"};
527        } elsif (defined $opt->{$pbos->{'family'}}) {
528                $param = $opt->{$pbos->{'family'}};
529        } elsif (defined $opt->{$pbos->{'type'}}) {
530                $param = $opt->{$pbos->{'type'}};
531        } elsif (defined $opt->{$pbos->{'os'}}) {
532                $param = $opt->{$pbos->{'os'}};
533        } elsif (defined $opt->{"default"}) {
534                $param = $opt->{"default"};
535        } else {
536                $param = "";
537        }
538
539        # Allow replacement of variables inside the parameter such as name, version, arch for rpmbootstrap
540        # but not shell variable which are backslashed
541        if ($param =~ /[^\\]\$/) {
542                pb_log(3,"Expanding variable on $param\n");
543                eval { $param =~ s/(\$\w+->{\'\w+\'})/$1/eeg };
544        }
545        push @param,$param;
546}
547
548pb_log(2,"DEBUG: pb_distro_get_param on $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'} returns ==".Dumper(@param)."==\n");
549
550# Return one param in scalar context, an array if not.
551my $nb = @param;
552if ($nb eq 1) {
553        return($param);
554} else {
555        return(@param);
556}
557}
558
559=item B<pb_distro_get_context>
560
561This function gets the OS context passed as parameter and return the corresponding distribution hash
562
563=cut
564
565
566sub pb_distro_get_context {
567
568my $os = shift;
569my $pbos;
570
571if (defined $os) {
572        my ($name,$ver,$darch) = split(/-/,$os);
573        pb_log(0,"Bad format for $os") if ((not defined $name) || (not defined $ver) || (not defined $darch)) ;
574        chomp($darch);
575        $pbos = pb_distro_init($name,$ver,$darch);
576} else {
577        $pbos = pb_distro_init();
578}
579return($pbos);
580}
581
582=back
583
584=head1 WEB SITES
585
586The 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/>.
587
588=head1 USER MAILING LIST
589
590None exists for the moment.
591
592=head1 AUTHORS
593
594The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
595
596=head1 COPYRIGHT
597
598Project-Builder.org is distributed under the GPL v2.0 license
599described in the file C<COPYING> included with the distribution.
600
601=cut
602
603
6041;
Note: See TracBrowser for help on using the repository browser.