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

Last change on this file since 702 was 702, checked in by Bruno Cornec, 15 years ago
  • 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
File size: 16.8 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";
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.