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

Last change on this file since 751 was 751, checked in by Bruno Cornec, 15 years ago
  • Adds snapshot support for VEs
  • Force usage of correct arch in pb_distro_init to avoid issues when cross building
File size: 16.7 KB
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";
79my $arch = shift || undef;
80
81# If we don't know which distribution we're on, then guess it
82($ddir,$dver) = pb_distro_get() if ((not defined $ddir) || (not defined $dver));
83
84# There should be unicity of names between ddir dfam and dtype
85# In case of duplicate, bad things can happen
86if (($ddir =~ /debian/) ||
87 ($ddir =~ /ubuntu/)) {
88 $dfam="du";
89 $dtype="deb";
90 $dsuf=".$ddir$dver";
91 # Chaining the commands allow to only test for what is able o be installed,
92 # not the update of the repo which may well be unaccessible if too old
93 $dupd="sudo apt-get update ; sudo apt-get -y install ";
94} elsif ($ddir =~ /gentoo/) {
95 $dfam="gen";
96 $dtype="ebuild";
97 $dver="nover";
98 $dsuf=".$ddir";
99 $dupd="sudo emerge ";
100} elsif ($ddir =~ /slackware/) {
101 $dfam="slack";
102 $dtype="tgz";
103 $dsuf=".$dfam$dver";
104} elsif (($ddir =~ /suse/) ||
105 ($ddir =~ /sles/)) {
106 $dfam="novell";
107 $dtype="rpm";
108 $dsuf=".$ddir$dver";
109 $dupd="export TERM=linux ; export PATH=\$PATH:/sbin:/usr/sbin ; sudo yast2 -i ";
110} elsif (($ddir =~ /redhat/) ||
111 ($ddir =~ /rhel/) ||
112 ($ddir =~ /fedora/) ||
113 ($ddir =~ /vmware/) ||
114 ($ddir =~ /asianux/) ||
115 ($ddir =~ /centos/)) {
116 $dfam="rh";
117 $dtype="rpm";
118 my $dver1 = $dver;
119 $dver1 =~ s/\.//;
120
121 # By defaut propose yum
122 $arch=pb_get_arch() if (not defined $arch);
123 my $opt = "";
124 if ($arch eq "x86_64") {
125 $opt="--exclude=*.i?86";
126 }
127 $dupd="sudo yum clean all; sudo yum update ; sudo yum -y $opt install ";
128 if ($ddir =~ /fedora/) {
129 $dsuf=".fc$dver1";
130 } elsif ($ddir =~ /redhat/) {
131 $dsuf=".rh$dver1";
132 $dupd="unknown";
133 } elsif ($ddir =~ /asianux/) {
134 $dsuf=".asianux$dver1";
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 'asianux' => 'asianux-release', # >= 2.2
206# Untested
207 'knoppix' => 'knoppix_version', #
208 'yellowdog' => 'yellowdog-release', #
209 'esmith' => 'e-smith-release', #
210 'turbolinux' => 'turbolinux-release', #
211 'blackcat' => 'blackcat-release', #
212 'aurox' => 'aurox-release', #
213 'annvix' => 'annvix-release', #
214 'cobalt' => 'cobalt-release', #
215 'redflag' => 'redflag-release', #
216 'ark' => 'ark-release', #
217 'pld' => 'pld-release', #
218 'nld' => 'nld-release', #
219 'lfs' => 'lfs-release', #
220 'mk' => 'mk-release', #
221 'conectiva' => 'conectiva-release', #
222 'immunix' => 'immunix-release', #
223 'tinysofa' => 'tinysofa-release', #
224 'trustix' => 'trustix-release', #
225 'adamantix' => 'adamantix_version', #
226 'yoper' => 'yoper-release', #
227 'arch' => 'arch-release', #
228 'libranet' => 'libranet_version', #
229 'valinux' => 'va-release', #
230 'yellowdog' => 'yellowdog-release', #
231 'ultrapenguin' => 'ultrapenguin-release', #
232 );
233
234# List of files that ambiguously indicates what distro we have
235my %ambiguous_rel_files = (
236 'mandrake' => 'mandrake-release', # <= 10.1
237 'debian' => 'debian_version', # >= 3.1
238 'suse' => 'SuSE-release', # >= 10.0
239 'redhat' => 'redhat-release', # >= 7.3
240 'lsb' => 'lsb-release', # ???
241 );
242
243# Should have the same keys as the previous one.
244# If ambiguity, which other distributions should be checked
245my %distro_similar = (
246 'mandrake' => ['mandrake', 'mandrakelinux'],
247 'debian' => ['debian', 'ubuntu'],
248 'suse' => ['suse', 'sles', 'opensuse'],
249 'redhat' => ['redhat', 'rhel', 'centos', 'mandrake', 'vmware'],
250 'lsb' => ['ubuntu', 'lsb'],
251 );
252
253my %distro_match = (
254# Tested
255 'gentoo' => '.* version (.+)',
256 'slackware' => 'S[^ ]* (.+)$',
257# There should be no ambiguity between potential ambiguous distro
258 'mandrakelinux' => 'Mandrakelinux release (.+) \(',
259 'mandrake' => 'Mandr[^ ]* release (.+) \(',
260 'mandriva' => 'Mandr[^ ]* [^ ]* release (.+) \(',
261 'fedora' => 'Fedora .*release (\d+) \(',
262 'vmware' => 'VMware ESX Server (\d+) \(',
263 'rhel' => 'Red Hat (?:Enterprise Linux|Linux Advanced Server) .*release ([0-9.]+).* \(',
264 'centos' => '.*CentOS .*release ([0-9]).* ',
265 'redhat' => 'Red Hat Linux release (.+) \(',
266 'sles' => 'SUSE .* Enterprise Server (\d+) \(',
267 'suse' => 'SUSE LINUX (\d.+) \(',
268 'opensuse' => 'openSUSE (\d.+) \(',
269 'asianux' => 'Asianux (?:Server|release) ([0-9]).* \(',
270 'lsb' => '.*[^Ubunt].*\nDISTRIB_RELEASE=(.+)',
271# Ubuntu includes a /etc/debian_version file that creates an ambiguity with debian
272# So we need to look at distros in reverse alphabetic order to treat ubuntu always first
273 'ubuntu' => '.*Ubuntu.*\nDISTRIB_RELEASE=(.+)',
274 'debian' => '(.+)',
275# Not tested
276 'arch' => '.* ([0-9.]+) .*',
277 'redflag' => 'Red Flag (?:Desktop|Linux) (?:release |\()(.*?)(?: \(.+)?\)',
278);
279
280my $release;
281my $distro;
282
283# Begin to test presence of non-ambiguous files
284# that way we reduce the choice
285my ($d,$r);
286while (($d,$r) = each %single_rel_files) {
287 if (-f "$base/$r" && ! -l "$base/$r") {
288 my $tmp=pb_get_content("$base/$r");
289 # Found the only possibility.
290 # Try to get version and return
291 if (defined ($distro_match{$d})) {
292 ($release) = $tmp =~ m/$distro_match{$d}/m;
293 } else {
294 print STDERR "Unable to find $d version in $r\n";
295 print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
296 $release = "unknown";
297 }
298 return($d,$release);
299 }
300}
301
302# Now look at ambiguous files
303# Ubuntu includes a /etc/debian_version file that creates an ambiguity with debian
304# So we need to look at distros in reverse alphabetic order to treat ubuntu always first via lsb
305foreach $d (reverse keys %ambiguous_rel_files) {
306 $r = $ambiguous_rel_files{$d};
307 if (-f "$base/$r" && !-l "$base/$r") {
308 # Found one possibility.
309 # Get all distros concerned by that file
310 my $tmp=pb_get_content("$base/$r");
311 my $found = 0;
312 my $ptr = $distro_similar{$d};
313 pb_log(2,"amb: ".Dumper($ptr)."\n");
314 $release = "unknown";
315 foreach my $dd (@$ptr) {
316 pb_log(2,"check $dd\n");
317 # Try to check pattern
318 if (defined $distro_match{$dd}) {
319 pb_log(2,"cmp: $distro_match{$dd} - vs - $tmp\n");
320 ($release) = $tmp =~ m/$distro_match{$dd}/m;
321 if ((defined $release) && ($release ne "unknown")) {
322 $distro = $dd;
323 $found = 1;
324 last;
325 }
326 }
327 }
328 if ($found == 0) {
329 print STDERR "Unable to find $d version in $r\n";
330 print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
331 $release = "unknown";
332 } else {
333 return($distro,$release);
334 }
335 }
336}
337return("unknown","unknown");
338}
339
340
341=over 4
342
343=item B<pb_distro_installdeps>
344
345This function install the dependencies required to build the package on an RPM based distro
346dependencies can be passed as a prameter in which case they are not computed
347
348=cut
349
350sub pb_distro_installdeps {
351
352# SPEC file
353my $f = shift || undef;
354my $dtype = shift || undef;
355my $dupd = shift || undef;
356my $deps = shift || undef;
357
358# Protection
359return if (not defined $dupd);
360
361# Get dependecies in the build file if not forced
362$deps = pb_distro_getdeps("$f", $dtype) if (not defined $deps);
363pb_log(2,"deps: $deps\n");
364return if ((not defined $deps) || ($deps =~ /^\s*$/));
365if ($deps !~ /^[ ]*$/) {
366 pb_system("$dupd $deps","Installing dependencies ($deps)");
367 }
368}
369
370=over 4
371
372=item B<pb_distro_getdeps>
373
374This function computes the dependencies indicated in the build file and return them as a string of packages to install
375
376=cut
377
378sub pb_distro_getdeps {
379
380my $f = shift || undef;
381my $dtype = shift || undef;
382
383my $regexp = "";
384my $deps = "";
385my $sep = $/;
386
387pb_log(3,"entering pb_distro_getdeps: $dtype - $f\n");
388# Protection
389return("") if (not defined $dtype);
390if ($dtype eq "rpm") {
391 # In RPM this could include files, but we do not handle them atm.
392 $regexp = '^BuildRequires:(.*)$';
393} elsif ($dtype eq "deb") {
394 $regexp = '^Build-Depends:(.*)$';
395} elsif ($dtype eq "ebuild") {
396 $sep = '"'.$/;
397 $regexp = '^DEPEND="(.*)"\n'
398} else {
399 # No idea
400 return("");
401}
402pb_log(2,"regexp: $regexp\n");
403
404
405# Protection
406return("") if (not defined $f);
407
408# Preserve separator before using the one we need
409my $oldsep = $/;
410$/ = $sep;
411open(DESC,"$f") || die "Unable to open $f";
412while (<DESC>) {
413 pb_log(4,"read: $_\n");
414 next if (! /$regexp/);
415 chomp();
416 # What we found with the regexp is the list of deps.
417 pb_log(2,"found deps: $_\n");
418 s/$regexp/$1/i;
419 # Remove conditions in the middle and at the end for deb
420 s/\(\s*[><=]+.*\)\s*,/,/g;
421 s/\(\s*[><=]+.*$//g;
422 # Same for rpm
423 s/[><=]+.*,/,/g;
424 s/[><=]+.*$//g;
425 # Improve string format (remove , and spaces at start, end and in double
426 s/,/ /g;
427 s/^\s*//;
428 s/\s*$//;
429 s/\s+/ /g;
430 $deps .= " ".$_;
431}
432close(DESC);
433$/ = $oldsep;
434pb_log(2,"now deps: $deps\n");
435my $deps2 = pb_distro_only_deps_needed($dtype,$deps);
436return($deps2);
437}
438
439
440=over 4
441
442=item B<pb_distro_only_deps_needed>
443
444This function returns only the dependencies not yet installed
445
446=cut
447
448sub pb_distro_only_deps_needed {
449
450my $dtype = shift || undef;
451my $deps = shift || undef;
452
453return("") if ((not defined $deps) || ($deps =~ /^\s*$/));
454my $deps2 = "";
455# Avoid to install what is already there
456foreach my $p (split(/ /,$deps)) {
457 if ($dtype eq "rpm") {
458 my $res = pb_system("rpm -q --whatprovides --quiet $p","","quiet");
459 next if ($res eq 0);
460 } elsif ($dtype eq "deb") {
461 my $res = pb_system("dpkg -L $p","","quiet");
462 next if ($res eq 0);
463 } elsif ($dtype eq "ebuild") {
464 } else {
465 # Not reached
466 }
467 pb_log(2,"found deps2: $p\n");
468 $deps2 .= " $p";
469}
470
471$deps2 =~ s/^\s*//;
472pb_log(2,"now deps2: $deps2\n");
473return($deps2);
474}
475
476=over 4
477
478=item B<pb_distro_setuprepo>
479
480This function sets up potential additional repository to the build environment
481
482=cut
483
484sub pb_distro_setuprepo {
485
486my $ddir = shift || undef;
487my $dver = shift;
488my $darch = shift;
489my $dtype = shift || undef;
490
491my ($addrepo) = pb_conf_read("$ENV{'PBDESTDIR'}/pbrc","addrepo");
492return if (not defined $addrepo);
493
494my $param = pb_distro_get_param($ddir,$dver,$darch,$addrepo);
495return if ($param eq "");
496
497# Loop on the list of additional repo
498foreach my $i (split(/,/,$param)) {
499
500 my ($scheme, $account, $host, $port, $path) = pb_get_uri($i);
501 my $bn = basename($i);
502
503 # The repo file can be local or remote. download or copy at the right place
504 if (($scheme eq "ftp") || ($scheme eq "http")) {
505 pb_system("wget -O $ENV{'PBTMP'}/$bn $i","Donwloading additional repository file $i");
506 } else {
507 copy($i,$ENV{'PBTMP'}/$bn);
508 }
509
510 # The repo file can be a real file or a package
511 if ($dtype eq "rpm") {
512 if ($bn =~ /\.rpm$/) {
513 my $pn = $bn;
514 $pn =~ s/\.rpm//;
515 if (pb_system("rpm -q --quiet $pn","","quiet") != 0) {
516 pb_system("sudo rpm -Uvh $ENV{'PBTMP'}/$bn","Adding package to setup repository");
517 }
518 } elsif ($bn =~ /\.repo$/) {
519 # Yum repo
520 pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/yum.repos.d","Adding yum repository") if (not -f "/etc/yum.repos.d/$bn");
521 } elsif ($bn =~ /\.addmedia/) {
522 # URPMI repo
523 # We should test that it's not already a urpmi repo
524 pb_system("chmod 755 $ENV{'PBTMP'}/$bn ; sudo $ENV{'PBTMP'}/$bn 2>&1 > /dev/null","Adding urpmi repository");
525 } else {
526 pb_log(0,"Unable to deal with repository file $i on rpm distro ! Please report to dev team\n");
527 }
528 } elsif ($dtype eq "deb") {
529 if (($bn =~ /\.sources.list$/) && (not -f "/etc/apt/sources.list.d/$bn")) {
530 pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/apt/sources.list.d","Adding apt repository");
531 pb_system("sudo apt-get update","Updating apt repository");
532 } else {
533 pb_log(0,"Unable to deal with repository file $i on deb distro ! Please report to dev team\n");
534 }
535 } else {
536 pb_log(0,"Unable to deal with repository file $i on that distro ! Please report to dev team\n");
537 }
538}
539return;
540}
541
542=over 4
543
544=item B<pb_distro_get_param>
545
546This function gets the parameter in the conf file from the most precise tuple up to default
547
548=cut
549
550sub pb_distro_get_param {
551
552my $param = "";
553my $ddir = shift;
554my $dver = shift;
555my $darch = shift;
556my $opt = shift;
557
558if (defined $opt->{"$ddir-$dver-$darch"}) {
559 $param = $opt->{"$ddir-$dver-$darch"};
560} elsif (defined $opt->{"$ddir-$dver"}) {
561 $param = $opt->{"$ddir-$dver"};
562} elsif (defined $opt->{"$ddir"}) {
563 $param = $opt->{"$ddir"};
564} elsif (defined $opt->{"default"}) {
565 $param = $opt->{"default"};
566} else {
567 $param = "";
568}
569return($param);
570
571}
572
573
574=back
575
576=head1 WEB SITES
577
578The 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/>.
579
580=head1 USER MAILING LIST
581
582None exists for the moment.
583
584=head1 AUTHORS
585
586The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
587
588=head1 COPYRIGHT
589
590Project-Builder.org is distributed under the GPL v2.0 license
591described in the file C<COPYING> included with the distribution.
592
593=cut
594
595
5961;
Note: See TracBrowser for help on using the repository browser.