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

Revision 702, 16.8 KB checked in by bruno, 4 years ago (diff)
  • new function pb_distro_setuprepo (addition of repository on the fly at build time)
  • pb_get_dist_param => pb_distro_get_param and placed in Distribution
  • removal of last locale issue
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::Base;
13use ProjectBuilder::Conf;
14use File::Basename;
15use File::Copy;
16
17# Inherit from the "Exporter" module which handles exporting functions.
18 
19use Exporter;
20 
21# Export, by default, all the functions into the namespace of
22# any code which uses this module.
23 
24our @ISA = qw(Exporter);
25our @EXPORT = qw(pb_distro_init pb_distro_get pb_distro_installdeps pb_distro_getdeps pb_distro_only_deps_needed pb_distro_setuprepo pb_distro_get_param);
26
27=pod
28
29=head1 NAME
30
31ProjectBuilder::Distribution, part of the project-builder.org - module dealing with distribution detection
32
33=head1 DESCRIPTION
34
35This modules provides functions to allow detection of Linux distributions, and giving back some attributes concerning them.
36
37=head1 SYNOPSIS
38
39  use ProjectBuilder::Distribution;
40
41  #
42  # Return information on the running distro
43  #
44  my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd) = pb_distro_init();
45  print "distro tuple: ".Dumper($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd)."\n";
46  #
47  # Return information on the requested distro
48  #
49  my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd) = pb_distro_init("ubuntu","7.10");
50  print "distro tuple: ".Dumper($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd)."\n";
51  #
52  # Return information on the running distro
53  #
54  my ($ddir,$dver) = pb_distro_get();
55  my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd) = pb_distro_init($ddir,$dver);
56  print "distro tuple: ".Dumper($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd)."\n";
57
58=head1 USAGE
59
60=over 4
61
62
63=item B<pb_distro_get>
64
65This 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.
66
67On my home machine it would currently report ("mandriva","2008.0").
68
69=cut
70
71sub pb_distro_init {
72
73my $ddir = shift || undef;
74my $dver = shift || undef;
75my $dfam = "unknown";
76my $dtype = "unknown";
77my $dsuf = "unknown";
78my $dupd = "unknown";
79
80# If we don't know which distribution we're on, then guess it
81($ddir,$dver) = pb_distro_get() if ((not defined $ddir) || (not defined $dver));
82
83# There should be unicity of names between ddir dfam and dtype
84# In case of duplicate, bad things can happen
85if (($ddir =~ /debian/) ||
86        ($ddir =~ /ubuntu/)) {
87        $dfam="du";
88        $dtype="deb";
89        $dsuf=".$ddir$dver";
90        # Chaining the commands allow to only test for what is able o be installed,
91        # not the update of the repo which may well be unaccessible if too old
92        $dupd="sudo apt-get update ; sudo apt-get -y install ";
93} elsif ($ddir =~ /gentoo/) {
94        $dfam="gen";
95        $dtype="ebuild";
96        $dver="nover";
97        $dsuf=".$ddir";
98        $dupd="sudo emerge ";
99} elsif ($ddir =~ /slackware/) {
100        $dfam="slack";
101        $dtype="tgz";
102        $dsuf=".$dfam$dver";
103} elsif (($ddir =~ /suse/) ||
104                ($ddir =~ /sles/)) {
105        if ($ddir =~ /opensuse/) {
106                $ddir = "suse";
107        }
108        $dfam="novell";
109        $dtype="rpm";
110        $dsuf=".$ddir$dver";
111        $dupd="export TERM=linux ; export PATH=\$PATH:/sbin:/usr/sbin ; sudo yast2 -i ";
112} elsif (($ddir =~ /redhat/) ||
113                ($ddir =~ /rhel/) ||
114                ($ddir =~ /fedora/) ||
115                ($ddir =~ /vmware/) ||
116                ($ddir =~ /centos/)) {
117        $dfam="rh";
118        $dtype="rpm";
119        my $dver1 = $dver;
120        $dver1 =~ s/\.//;
121
122        # By defaut propose yum
123        my $arch=`uname -m`;
124        my $opt = "";
125        chomp($arch);
126        if ($arch eq "x86_64") {
127                $opt="--exclude=*.i?86";
128        }
129        $dupd="sudo yum clean all; sudo yum update ; sudo yum -y $opt install ";
130        if ($ddir =~ /fedora/) {
131                $dsuf=".fc$dver1";
132        } elsif ($ddir =~ /redhat/) {
133                $dsuf=".rh$dver1";
134                $dupd="unknown";
135        } elsif ($ddir =~ /vmware/) {
136                $dsuf=".vwm$dver1";
137                $dupd="unknown";
138        } else {
139                # older versions of rhel ran up2date
140                if ((($dver eq "2.1") || ($dver eq "3") || ($dver eq "4")) && ($ddir eq "rhel")) {
141                        $dupd="sudo up2date -y ";
142                }
143                $dsuf=".$ddir$dver1";
144        }
145} elsif (($ddir =~ /mandrake/) ||
146                ($ddir =~ /mandrakelinux/) ||
147                ($ddir =~ /mandriva/)) {
148        $dfam="md";
149        $dtype="rpm";
150        if ($ddir =~ /mandrakelinux/) {
151                $ddir = "mandrake";
152        }
153        if ($ddir =~ /mandrake/) {
154                my $dver1 = $dver;
155                $dver1 =~ s/\.//;
156                $dsuf=".mdk$dver1";
157        } else {
158                $dsuf=".mdv$dver";
159        }
160        # Chaining the commands allow to only test for what is able o be installed,
161        # not the update of the repo which may well be unaccessible if too old
162        $dupd="sudo urpmi.update -a ; sudo urpmi --auto ";
163} elsif ($ddir =~ /freebsd/) {
164        $dfam="bsd";
165        $dtype="port";
166        my $dver1 = $dver;
167        $dver1 =~ s/\.//;
168        $dsuf=".$dfam$dver1";
169} else {
170        $dfam="unknown";
171}
172
173return($ddir, $dver, $dfam, $dtype, $dsuf, $dupd);
174}
175
176=item B<pb_distro_init>
177
178This function returns a list of 5 parameters indicating the distribution name, version, family, type of build system and suffix of packages of the underlying Linux distribution. The value of the 5 fields may be "unknown" in case the function was unable to recognize on which distribution it is running.
179
180As an example, Ubuntu and Debian are in the same "du" family. As well as RedHat, RHEL, CentOS, fedora are on the same "rh" family.
181Mandriva, Open SuSE and Fedora have all the same "rpm" type of build system. Ubuntu ad Debian have the same "deb" type of build system.
182And "fc" is the extension generated for all Fedora packages (Version will be added by pb).
183
184When 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.
185
186Cf: http://linuxmafia.com/faq/Admin/release-files.html
187Ideas taken from http://search.cpan.org/~kerberus/Linux-Distribution-0.14/lib/Linux/Distribution.pm
188
189=cut
190
191sub pb_distro_get {
192
193my $base="/etc";
194
195# List of files that unambiguously indicates what distro we have
196my %single_rel_files = (
197# Tested
198        'gentoo'                        =>      'gentoo-release',               # >= 1.6
199        'slackware'                     =>      'slackware-version',    # >= 10.2
200        'mandriva'                      =>      'mandriva-release',             # >=2006.0
201        'mandrakelinux'         =>      'mandrakelinux-release',# = 10.2
202        'fedora'                        =>      'fedora-release',               # >= 4
203        'vmware'                        =>      'vmware-release',               # >= 3
204        'sles'                          =>      'sles-release',                 # Doesn't exist as of 10
205# Untested
206        'knoppix'                       =>      'knoppix_version',              #
207        'yellowdog'                     =>      'yellowdog-release',    #
208        'esmith'                        =>      'e-smith-release',              #
209        'turbolinux'            =>      'turbolinux-release',   #
210        'blackcat'                      =>      'blackcat-release',             #
211        'aurox'                         =>      'aurox-release',                #
212        'annvix'                        =>      'annvix-release',               #
213        'cobalt'                        =>      'cobalt-release',               #
214        'redflag'                       =>      'redflag-release',              #
215        'ark'                           =>      'ark-release',                  #
216        'pld'                           =>      'pld-release',                  #
217        'nld'                           =>      'nld-release',                  #
218        'lfs'                           =>      'lfs-release',                  #
219        'mk'                            =>      'mk-release',                   #
220        'conectiva'                     =>      'conectiva-release',    #
221        'immunix'                       =>      'immunix-release',              #
222        'tinysofa'                      =>      'tinysofa-release',             #
223        'trustix'                       =>      'trustix-release',              #
224        'adamantix'                     =>      'adamantix_version',    #
225        'yoper'                         =>      'yoper-release',                #
226        'arch'                          =>      'arch-release',                 #
227        'libranet'                      =>      'libranet_version',             #
228        'valinux'                       =>      'va-release',                   #
229        'yellowdog'                     =>      'yellowdog-release',    #
230        'ultrapenguin'          =>      'ultrapenguin-release', #
231        );
232
233# List of files that ambiguously indicates what distro we have
234my %ambiguous_rel_files = (
235        'mandrake'                      =>      'mandrake-release',             # <= 10.1
236        'debian'                        =>      'debian_version',               # >= 3.1
237        'suse'                          =>      'SuSE-release',                 # >= 10.0
238        'redhat'                        =>      'redhat-release',               # >= 7.3
239        'lsb'                           =>      'lsb-release',                  # ???
240        );
241
242# Should have the same keys as the previous one.
243# If ambiguity, which other distributions should be checked
244my %distro_similar = (
245        'mandrake'                      => ['mandrake', 'mandrakelinux'],
246        'debian'                        => ['debian', 'ubuntu'],
247        'suse'                          => ['suse', 'sles', 'opensuse'],
248        'redhat'                        => ['redhat', 'rhel', 'centos', 'mandrake', 'vmware'],
249        'lsb'                           => ['ubuntu', 'lsb'],
250        );
251
252my %distro_match = (
253# Tested
254        'gentoo'                                => '.* version (.+)',
255        'slackware'                             => 'S[^ ]* (.+)$',
256# There should be no ambiguity between potential ambiguous distro
257        'mandrakelinux'                 => 'Mandrakelinux release (.+) \(',
258        'mandrake'                              => 'Mandr[^ ]* release (.+) \(',
259        'mandriva'                              => 'Mandr[^ ]* [^ ]* release (.+) \(',
260        'fedora'                                => 'Fedora .*release (\d+) \(',
261        'vmware'                                => 'VMware ESX Server (\d+) \(',
262        'rhel'                                  => 'Red Hat (?:Enterprise Linux|Linux Advanced Server) .*release ([0-9.]+).* \(',
263        'centos'                                => '.*CentOS .*release ([0-9]).* ',
264        'redhat'                                => 'Red Hat Linux release (.+) \(',
265        'sles'                                  => 'SUSE .* Enterprise Server (\d+) \(',
266        'suse'                                  => 'SUSE LINUX (\d.+) \(',
267        'opensuse'                              => 'openSUSE (\d.+) \(',
268        'lsb'                                   => '.*[^Ubunt].*\nDISTRIB_RELEASE=(.+)',
269# Ubuntu includes a /etc/debian_version file that cretaes an ambiguity with debian
270# So we need to look at distros in reverse alphabetic order to treat ubuntu always first
271        'ubuntu'                                => '.*Ubuntu.*\nDISTRIB_RELEASE=(.+)',
272        'debian'                                => '(.+)',
273# Not tested
274        'arch'                                  => '.* ([0-9.]+) .*',
275        'redflag'                               => 'Red Flag (?:Desktop|Linux) (?:release |\()(.*?)(?: \(.+)?\)',
276);
277
278my $release;
279my $distro;
280
281# Begin to test presence of non-ambiguous files
282# that way we reduce the choice
283my ($d,$r);
284while (($d,$r) = each %single_rel_files) {
285        if (-f "$base/$r" && ! -l "$base/$r") {
286                my $tmp=pb_get_content("$base/$r");
287                # Found the only possibility.
288                # Try to get version and return
289                if (defined ($distro_match{$d})) {
290                        ($release) = $tmp =~ m/$distro_match{$d}/m;
291                } else {
292                        print STDERR "Unable to find $d version in $r\n";
293                        print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
294                        $release = "unknown";
295                }
296                return($d,$release);
297        }
298}
299
300# Now look at ambiguous files
301# Ubuntu includes a /etc/debian_version file that creates an ambiguity with debian
302# So we need to look at distros in reverse alphabetic order to treat ubuntu always first via lsb
303foreach $d (reverse keys %ambiguous_rel_files) {
304        $r = $ambiguous_rel_files{$d};
305        if (-f "$base/$r" && !-l "$base/$r") {
306                # Found one possibility.
307                # Get all distros concerned by that file
308                my $tmp=pb_get_content("$base/$r");
309                my $found = 0;
310                my $ptr = $distro_similar{$d};
311                pb_log(2,"amb: ".Dumper($ptr)."\n");
312                $release = "unknown";
313                foreach my $dd (@$ptr) {
314                        pb_log(2,"check $dd\n");
315                        # Try to check pattern
316                        if (defined $distro_match{$dd}) {
317                                pb_log(2,"cmp: $distro_match{$dd} - vs - $tmp\n");
318                                ($release) = $tmp =~ m/$distro_match{$dd}/m;
319                                if ((defined $release) && ($release ne "unknown")) {
320                                        $distro = $dd;
321                                        $found = 1;
322                                        last;
323                                }
324                        }
325                }
326                if ($found == 0) {
327                        print STDERR "Unable to find $d version in $r\n";
328                        print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
329                        $release = "unknown";
330                } else {
331                        return($distro,$release);
332                }
333        }
334}
335return("unknown","unknown");
336}
337
338
339=over 4
340
341=item B<pb_distro_installdeps>
342
343This function install the dependencies required to build the package on an RPM based distro
344dependencies can be passed as a prameter in which case they are not computed
345
346=cut
347
348sub pb_distro_installdeps {
349
350# SPEC file
351my $f = shift || undef;
352my $dtype = shift || undef;
353my $dupd = shift || undef;
354my $deps = shift || undef;
355
356# Protection
357return if (not defined $dupd);
358
359# Get dependecies in the build file if not forced
360$deps = pb_distro_getdeps("$f", $dtype) if (not defined $deps);
361pb_log(2,"deps: $deps\n");
362return if ((not defined $deps) || ($deps =~ /^\s*$/));
363if ($deps !~ /^[        ]*$/) {
364        pb_system("$dupd $deps","Installing dependencies ($deps)");
365        }
366}
367
368=over 4
369
370=item B<pb_distro_getdeps>
371
372This function computes the dependencies indicated in the build file and return them as a string of packages to install
373
374=cut
375
376sub pb_distro_getdeps {
377
378my $f = shift || undef;
379my $dtype = shift || undef;
380
381my $regexp = "";
382my $deps = "";
383my $sep = $/;
384
385pb_log(3,"entering pb_distro_getdeps: $dtype - $f\n");
386# Protection
387return("") if (not defined $dtype);
388if ($dtype eq  "rpm") {
389        # In RPM this could include files, but we do not handle them atm.
390        $regexp = '^BuildRequires:(.*)$';
391} elsif ($dtype eq "deb") {
392        $regexp = '^Build-Depends:(.*)$';
393} elsif ($dtype eq "ebuild") {
394        $sep = '"'.$/;
395        $regexp = '^DEPEND="(.*)"\n'
396} else {
397        # No idea
398        return("");
399}
400pb_log(2,"regexp: $regexp\n");
401
402
403# Protection
404return("") if (not defined $f);
405
406# Preserve separator before using the one we need
407my $oldsep = $/;
408$/ = $sep;
409open(DESC,"$f") || die "Unable to open $f";
410while (<DESC>) {
411        pb_log(4,"read: $_\n");
412        next if (! /$regexp/);
413        chomp();
414        # What we found with the regexp is the list of deps.
415        pb_log(2,"found deps: $_\n");
416        s/$regexp/$1/i;
417        # Remove conditions in the middle and at the end for deb
418        s/\(\s*[><=]+.*\)\s*,/,/g;
419        s/\(\s*[><=]+.*$//g;
420        # Same for rpm
421        s/[><=]+.*,/,/g;
422        s/[><=]+.*$//g;
423        # Improve string format (remove , and spaces at start, end and in double
424        s/,/ /g;
425        s/^\s*//;
426        s/\s*$//;
427        s/\s+/ /g;
428        $deps .= " ".$_;
429}
430close(DESC);
431$/ = $oldsep;
432pb_log(2,"now deps: $deps\n");
433my $deps2 = pb_distro_only_deps_needed($dtype,$deps);
434return($deps2);
435}
436
437
438=over 4
439
440=item B<pb_distro_only_deps_needed>
441
442This function returns only the dependencies not yet installed
443
444=cut
445
446sub pb_distro_only_deps_needed {
447
448my $dtype = shift || undef;
449my $deps = shift || undef;
450
451return("") if ((not defined $deps) || ($deps =~ /^\s*$/));
452my $deps2 = "";
453# Avoid to install what is already there
454foreach my $p (split(/ /,$deps)) {
455        if ($dtype eq  "rpm") {
456                my $res = pb_system("rpm -q --whatprovides --quiet $p","","quiet");
457                next if ($res eq 0);
458        } elsif ($dtype eq "deb") {
459                my $res = pb_system("dpkg -L $p","","quiet");
460                next if ($res eq 0);
461        } elsif ($dtype eq "ebuild") {
462        } else {
463                # Not reached
464        }
465        pb_log(2,"found deps2: $p\n");
466        $deps2 .= " $p";
467}
468
469$deps2 =~ s/^\s*//;
470pb_log(2,"now deps2: $deps2\n");
471return($deps2);
472}
473
474=over 4
475
476=item B<pb_distro_setuprepo>
477
478This function sets up potential additional repository to the build environment
479
480=cut
481
482sub pb_distro_setuprepo {
483
484my $ddir = shift || undef;
485my $dver = shift;
486my $darch = shift;
487my $dtype = shift || undef;
488
489my ($addrepo) = pb_conf_read("$ENV{'PBDESTDIR'}/pbrc","addrepo");
490return if (not defined $addrepo);
491
492my $param = pb_distro_get_param($ddir,$dver,$darch,$addrepo);
493return if ($param eq "");
494
495# Loop on the list of additional repo
496foreach my $i (split(/,/,$param)) {
497
498        my ($scheme, $account, $host, $port, $path) = pb_get_uri($i);
499        my $bn = basename($i);
500
501        # The repo file can be local or remote. download or copy at the right place
502        if (($scheme eq "ftp") || ($scheme eq "http")) {
503                pb_system("wget -O $ENV{'PBTMP'}/$bn $i","Donwloading additional repository file $i");
504        } else {
505                copy($i,$ENV{'PBTMP'}/$bn);
506        }
507
508        # The repo file can be a real file or a package
509        if ($dtype eq "rpm") {
510                if ($bn =~ /\.rpm$/) {
511                pb_system("sudo rpm -Uvh $ENV{'PBTMP'}/$bn","Adding package to setup repostory");
512        } elsif ($bn =~ /\.repo$/) {
513                        # Yum repo
514                        pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/yum.repo.d","Adding yum repository");
515                } elsif ($bn =~ /\.addmedia/) {
516                        # URPMI repo
517                        pb_system("chmod 755 $ENV{'PBTMP'}/$bn ; sudo $ENV{'PBTMP'}/$bn 2>&1 > /dev/null","Adding urpmi repository");
518                } else {
519                        pb_log(0,"Unable to deal with repository file $i on rpm distro ! Please report to dev team\n");
520                }
521        } elsif ($dtype eq "deb") {
522                if ($bn =~ /\.sources.list$/) {
523                        my $aptrepo = "";
524                        open(REPO,"$ENV{'PBTMP'}/$bn") || die "Unable to open $ENV{'PBTMP'}/$bn";
525                        while (my $repo=<REPO>) {
526                                my $found = 0;
527                                open(APT,"/etc/apt/sources.list") || die "Unable to open /etc/apt/sources.list";
528                                while (my $apt=<APT>) {
529                                        $found++ if ($apt =~ /$repo/);
530                                }
531                                close(APT);
532                                $aptrepo .= "$repo\n" if ($found == 0);
533                        }
534                        close(REPO);
535                        if ($aptrepo ne "") {
536                                pb_system("sudo echo # Added by project-builder.org >> /etc/apt/sources.list");
537                                pb_system("sudo echo #  >> /etc/apt/sources.list");
538                                pb_system("sudo echo \'$aptrepo\' >> /etc/apt/sources.list");
539                        }
540                        pb_system("sudo apt-get update","Adding apt repository");
541                } else {
542                        pb_log(0,"Unable to deal with repository file $i on deb distro ! Please report to dev team\n");
543                }
544        } else {
545                pb_log(0,"Unable to deal with repository file $i on that distro ! Please report to dev team\n");
546        }
547}
548return;
549}
550
551=over 4
552
553=item B<pb_distro_get_param>
554
555This function gets the parameter in the conf file from the most precise tuple up to default
556
557=cut
558
559sub pb_distro_get_param {
560
561my $param = "";
562my $ddir = shift;
563my $dver = shift;
564my $darch = shift;
565my $opt = shift;
566
567if (defined $opt->{"$ddir-$dver-$darch"}) {
568        $param = $opt->{"$ddir-$dver-$darch"};
569} elsif (defined $opt->{"$ddir-$dver"}) {
570        $param = $opt->{"$ddir-$dver"};
571} elsif (defined $opt->{"$ddir"}) {
572        $param = $opt->{"$ddir"};
573} elsif (defined $opt->{"default"}) {
574        $param = $opt->{"default"};
575} else {
576        $param = "";
577}
578return($param);
579
580}
581=back
582
583=head1 WEB SITES
584
585The 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/>.
586
587=head1 USER MAILING LIST
588
589None exists for the moment.
590
591=head1 AUTHORS
592
593The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
594
595=head1 COPYRIGHT
596
597Project-Builder.org is distributed under the GPL v2.0 license
598described in the file C<COPYING> included with the distribution.
599
600=cut
601
602
6031;
Note: See TracBrowser for help on using the repository browser.