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

Last change on this file since 867 was 867, checked in by Bruno Cornec, 15 years ago
  • Transition static distribution into a configuration file typically /etc/pb/pb.conf
  • function pb_distro_init adapted to use the new conf file
  • function pb_distro_get_param adapted to support osfamily and ostype
  • Adapt the package building of pb to the need of the new configuration file
File size: 16.0 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, $arch) = pb_distro_init();
45 print "distro tuple: ".Dumper($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch)."\n";
46 #
47 # Return information on the requested distro
48 #
49 my ($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch) = pb_distro_init("ubuntu","7.10","x86_64");
50 print "distro tuple: ".Dumper($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch)."\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, $arch) = pb_distro_init($ddir,$dver);
56 print "distro tuple: ".Dumper($ddir, $dver, $dfam, $dtype, $pbsuf, $pbupd, $arch)."\n";
57
58=head1 USAGE
59
60=over 4
61
62=item B<pb_distro_init>
63
64This function returns a list of 7 parameters indicating the distribution name, version, family, type of build system, suffix of packages, update command line and architecture of the underlying Linux distribution. The value of the 7 fields may be "unknown" in case the function was unable to recognize on which distribution it is running.
65
66As an example, Ubuntu and Debian are in the same "du" family. As well as RedHat, RHEL, CentOS, fedora are on the same "rh" family.
67Mandriva, Open SuSE and Fedora have all the same "rpm" type of build system. Ubuntu ad Debian have the same "deb" type of build system.
68And "fc" is the extension generated for all Fedora packages (Version will be added by pb).
69
70When 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.
71
72Cf: http://linuxmafia.com/faq/Admin/release-files.html
73Ideas taken from http://search.cpan.org/~kerberus/Linux-Distribution-0.14/lib/Linux/Distribution.pm
74
75=cut
76
77
78sub pb_distro_init {
79
80my $ddir = shift || undef;
81my $dver = shift || undef;
82my $dfam = "unknown";
83my $dtype = "unknown";
84my $dsuf = "unknown";
85my $dupd = "unknown";
86my $darch = shift || undef;
87my $dnover = "false";
88my $drmdot = "false";
89
90# If we don't know which distribution we're on, then guess it
91($ddir,$dver) = pb_distro_get() if ((not defined $ddir) || (not defined $dver));
92
93# Initialize arch
94$darch=pb_get_arch() if (not defined $darch);
95
96# Adds conf file for distribution description
97# the location of the conf file is finalyzed at install time
98# depending whether we deal with package install or tar file install
99pb_conf_add("CCCC/pb.conf");
100my ($osfamily,$ostype,$osupd,$ossuffix,$osnover,$osremovedotinver) = pb_conf_get("osfamily","ostype","osupd","ossuffix","osnover","osremovedotinver");
101
102$dfam = pb_distro_get_param($ddir,$dver,$darch,$osfamily);
103$dtype = $ostype->{$dfam} if (defined $ostype->{$dfam});
104$dupd = pb_distro_get_param($ddir,$dver,$darch,$osupd,$dfam,$dtype);
105$dsuf = pb_distro_get_param($ddir,$dver,$darch,$ossuffix,$dfam,$dtype);
106$dnover = pb_distro_get_param($ddir,$dver,$darch,$osnover,$dfam,$dtype);
107$drmdot = pb_distro_get_param($ddir,$dver,$darch,$osremovedotinver,$dfam,$dtype);
108
109# Some OS have no interesting version
110$dver = "nover" if ($dnover eq "true");
111
112# For some OS remove the . in version name
113$dver =~ s/\.// if ($drmdot eq "true");
114
115if ((not defined $dsuf) || ($dsuf eq "unknown")) {
116 # By default suffix is a concatenation of .ddir and dver
117 $dsuf = ".$ddir$dver"
118} else {
119 # concat just the version to what has been found
120 $dsuf = ".$dsuf$dver";
121}
122
123# if ($arch eq "x86_64") {
124# $opt="--exclude=*.i?86";
125# }
126
127return($ddir, $dver, $dfam, $dtype, $dsuf, $dupd, $darch);
128}
129
130=item B<pb_distro_get>
131
132This 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.
133
134On my home machine it would currently report ("mandriva","2009.0").
135
136=cut
137
138sub pb_distro_get {
139
140my $base="/etc";
141
142# List of files that unambiguously indicates what distro we have
143my %single_rel_files = (
144# Tested
145 'gentoo' => 'gentoo-release', # >= 1.6
146 'slackware' => 'slackware-version', # >= 10.2
147 'mandriva' => 'mandriva-release', # >=2006.0
148 'mandrakelinux' => 'mandrakelinux-release',# = 10.2
149 'fedora' => 'fedora-release', # >= 4
150 'vmware' => 'vmware-release', # >= 3
151 'sles' => 'sles-release', # Doesn't exist as of 10
152 'asianux' => 'asianux-release', # >= 2.2
153# Untested
154 'knoppix' => 'knoppix_version', #
155 'yellowdog' => 'yellowdog-release', #
156 'esmith' => 'e-smith-release', #
157 'turbolinux' => 'turbolinux-release', #
158 'blackcat' => 'blackcat-release', #
159 'aurox' => 'aurox-release', #
160 'annvix' => 'annvix-release', #
161 'cobalt' => 'cobalt-release', #
162 'redflag' => 'redflag-release', #
163 'ark' => 'ark-release', #
164 'pld' => 'pld-release', #
165 'nld' => 'nld-release', #
166 'lfs' => 'lfs-release', #
167 'mk' => 'mk-release', #
168 'conectiva' => 'conectiva-release', #
169 'immunix' => 'immunix-release', #
170 'tinysofa' => 'tinysofa-release', #
171 'trustix' => 'trustix-release', #
172 'adamantix' => 'adamantix_version', #
173 'yoper' => 'yoper-release', #
174 'arch' => 'arch-release', #
175 'libranet' => 'libranet_version', #
176 'valinux' => 'va-release', #
177 'yellowdog' => 'yellowdog-release', #
178 'ultrapenguin' => 'ultrapenguin-release', #
179 );
180
181# List of files that ambiguously indicates what distro we have
182my %ambiguous_rel_files = (
183 'mandrake' => 'mandrake-release', # <= 10.1
184 'debian' => 'debian_version', # >= 3.1
185 'suse' => 'SuSE-release', # >= 10.0
186 'redhat' => 'redhat-release', # >= 7.3
187 'lsb' => 'lsb-release', # ???
188 );
189
190# Should have the same keys as the previous one.
191# If ambiguity, which other distributions should be checked
192my %distro_similar = (
193 'mandrake' => ['mandrake', 'mandrakelinux'],
194 'debian' => ['debian', 'ubuntu'],
195 'suse' => ['suse', 'sles', 'opensuse'],
196 'redhat' => ['redhat', 'rhel', 'centos', 'mandrake', 'vmware'],
197 'lsb' => ['ubuntu', 'lsb'],
198 );
199
200my %distro_match = (
201# Tested
202 'gentoo' => '.* version (.+)',
203 'slackware' => 'S[^ ]* (.+)$',
204# There should be no ambiguity between potential ambiguous distro
205 'mandrakelinux' => 'Mandrakelinux release (.+) \(',
206 'mandrake' => 'Mandr[^ ]* release (.+) \(',
207 'mandriva' => 'Mandr[^ ]* [^ ]* release (.+) \(',
208 'fedora' => 'Fedora .*release (\d+) \(',
209 'vmware' => 'VMware ESX Server (\d+) \(',
210 'rhel' => 'Red Hat (?:Enterprise Linux|Linux Advanced Server) .*release ([0-9.]+).* \(',
211 'centos' => '.*CentOS .*release ([0-9]).* ',
212 'redhat' => 'Red Hat Linux release (.+) \(',
213 'sles' => 'SUSE .* Enterprise Server (\d+) \(',
214 'suse' => 'SUSE LINUX (\d.+) \(',
215 'opensuse' => 'openSUSE (\d.+) \(',
216 'asianux' => 'Asianux (?:Server|release) ([0-9]).* \(',
217 'lsb' => '.*[^Ubunt].*\nDISTRIB_RELEASE=(.+)',
218# Ubuntu includes a /etc/debian_version file that creates an ambiguity with debian
219# So we need to look at distros in reverse alphabetic order to treat ubuntu always first
220 'ubuntu' => '.*Ubuntu.*\nDISTRIB_RELEASE=(.+)',
221 'debian' => '(.+)',
222# Not tested
223 'arch' => '.* ([0-9.]+) .*',
224 'redflag' => 'Red Flag (?:Desktop|Linux) (?:release |\()(.*?)(?: \(.+)?\)',
225);
226
227my $release;
228my $distro;
229
230# Begin to test presence of non-ambiguous files
231# that way we reduce the choice
232my ($d,$r);
233while (($d,$r) = each %single_rel_files) {
234 if (-f "$base/$r" && ! -l "$base/$r") {
235 my $tmp=pb_get_content("$base/$r");
236 # Found the only possibility.
237 # Try to get version and return
238 if (defined ($distro_match{$d})) {
239 ($release) = $tmp =~ m/$distro_match{$d}/m;
240 } else {
241 print STDERR "Unable to find $d version in $r\n";
242 print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
243 $release = "unknown";
244 }
245 return($d,$release);
246 }
247}
248
249# Now look at ambiguous files
250# Ubuntu 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
252foreach $d (reverse keys %ambiguous_rel_files) {
253 $r = $ambiguous_rel_files{$d};
254 if (-f "$base/$r" && !-l "$base/$r") {
255 # Found one possibility.
256 # Get all distros concerned by that file
257 my $tmp=pb_get_content("$base/$r");
258 my $found = 0;
259 my $ptr = $distro_similar{$d};
260 pb_log(2,"amb: ".Dumper($ptr)."\n");
261 $release = "unknown";
262 foreach my $dd (@$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 if ($found == 0) {
276 print STDERR "Unable to find $d version in $r\n";
277 print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
278 $release = "unknown";
279 } else {
280 return($distro,$release);
281 }
282 }
283}
284return("unknown","unknown");
285}
286
287
288=over 4
289
290=item B<pb_distro_installdeps>
291
292This function install the dependencies required to build the package on an RPM based distro
293dependencies can be passed as a parameter in which case they are not computed
294
295=cut
296
297sub pb_distro_installdeps {
298
299# SPEC file
300my $f = shift || undef;
301my $dtype = shift || undef;
302my $dupd = shift || undef;
303my $deps = shift || undef;
304
305# Protection
306return if (not defined $dupd);
307
308# Get dependecies in the build file if not forced
309$deps = pb_distro_getdeps("$f", $dtype) if (not defined $deps);
310pb_log(2,"deps: $deps\n");
311return if ((not defined $deps) || ($deps =~ /^\s*$/));
312if ($deps !~ /^[ ]*$/) {
313 pb_system("$dupd $deps","Installing dependencies ($deps)");
314 }
315}
316
317=over 4
318
319=item B<pb_distro_getdeps>
320
321This function computes the dependencies indicated in the build file and return them as a string of packages to install
322
323=cut
324
325sub pb_distro_getdeps {
326
327my $f = shift || undef;
328my $dtype = shift || undef;
329
330my $regexp = "";
331my $deps = "";
332my $sep = $/;
333
334pb_log(3,"entering pb_distro_getdeps: $dtype - $f\n");
335# Protection
336return("") if (not defined $dtype);
337if ($dtype eq "rpm") {
338 # In RPM this could include files, but we do not handle them atm.
339 $regexp = '^BuildRequires:(.*)$';
340} elsif ($dtype eq "deb") {
341 $regexp = '^Build-Depends:(.*)$';
342} elsif ($dtype eq "ebuild") {
343 $sep = '"'.$/;
344 $regexp = '^DEPEND="(.*)"\n'
345} else {
346 # No idea
347 return("");
348}
349pb_log(2,"regexp: $regexp\n");
350
351
352# Protection
353return("") if (not defined $f);
354
355# Preserve separator before using the one we need
356my $oldsep = $/;
357$/ = $sep;
358open(DESC,"$f") || die "Unable to open $f";
359while (<DESC>) {
360 pb_log(4,"read: $_\n");
361 next if (! /$regexp/);
362 chomp();
363 # What we found with the regexp is the list of deps.
364 pb_log(2,"found deps: $_\n");
365 s/$regexp/$1/i;
366 # Remove conditions in the middle and at the end for deb
367 s/\(\s*[><=]+.*\)\s*,/,/g;
368 s/\(\s*[><=]+.*$//g;
369 # Same for rpm
370 s/[><=]+.*,/,/g;
371 s/[><=]+.*$//g;
372 # Improve string format (remove , and spaces at start, end and in double
373 s/,/ /g;
374 s/^\s*//;
375 s/\s*$//;
376 s/\s+/ /g;
377 $deps .= " ".$_;
378}
379close(DESC);
380$/ = $oldsep;
381pb_log(2,"now deps: $deps\n");
382my $deps2 = pb_distro_only_deps_needed($dtype,$deps);
383return($deps2);
384}
385
386
387=over 4
388
389=item B<pb_distro_only_deps_needed>
390
391This function returns only the dependencies not yet installed
392
393=cut
394
395sub pb_distro_only_deps_needed {
396
397my $dtype = shift || undef;
398my $deps = shift || undef;
399
400return("") if ((not defined $deps) || ($deps =~ /^\s*$/));
401my $deps2 = "";
402# Avoid to install what is already there
403foreach my $p (split(/ /,$deps)) {
404 if ($dtype eq "rpm") {
405 my $res = pb_system("rpm -q --whatprovides --quiet $p","","quiet");
406 next if ($res eq 0);
407 } elsif ($dtype eq "deb") {
408 my $res = pb_system("dpkg -L $p","","quiet");
409 next if ($res eq 0);
410 } elsif ($dtype eq "ebuild") {
411 } else {
412 # Not reached
413 }
414 pb_log(2,"found deps2: $p\n");
415 $deps2 .= " $p";
416}
417
418$deps2 =~ s/^\s*//;
419pb_log(2,"now deps2: $deps2\n");
420return($deps2);
421}
422
423=over 4
424
425=item B<pb_distro_setuprepo>
426
427This function sets up potential additional repository to the build environment
428
429=cut
430
431sub pb_distro_setuprepo {
432
433my $ddir = shift || undef;
434my $dver = shift;
435my $darch = shift;
436my $dtype = shift || undef;
437
438my ($addrepo) = pb_conf_read("$ENV{'PBDESTDIR'}/pbrc","addrepo");
439return if (not defined $addrepo);
440
441my $param = pb_distro_get_param($ddir,$dver,$darch,$addrepo);
442return if ($param eq "");
443
444# Loop on the list of additional repo
445foreach my $i (split(/,/,$param)) {
446
447 my ($scheme, $account, $host, $port, $path) = pb_get_uri($i);
448 my $bn = basename($i);
449
450 # The repo file can be local or remote. download or copy at the right place
451 if (($scheme eq "ftp") || ($scheme eq "http")) {
452 pb_system("wget -O $ENV{'PBTMP'}/$bn $i","Donwloading additional repository file $i");
453 } else {
454 copy($i,$ENV{'PBTMP'}/$bn);
455 }
456
457 # The repo file can be a real file or a package
458 if ($dtype eq "rpm") {
459 if ($bn =~ /\.rpm$/) {
460 my $pn = $bn;
461 $pn =~ s/\.rpm//;
462 if (pb_system("rpm -q --quiet $pn","","quiet") != 0) {
463 pb_system("sudo rpm -Uvh $ENV{'PBTMP'}/$bn","Adding package to setup repository");
464 }
465 } elsif ($bn =~ /\.repo$/) {
466 # Yum repo
467 pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/yum.repos.d","Adding yum repository") if (not -f "/etc/yum.repos.d/$bn");
468 } elsif ($bn =~ /\.addmedia/) {
469 # URPMI repo
470 # We should test that it's not already a urpmi repo
471 pb_system("chmod 755 $ENV{'PBTMP'}/$bn ; sudo $ENV{'PBTMP'}/$bn 2>&1 > /dev/null","Adding urpmi repository");
472 } else {
473 pb_log(0,"Unable to deal with repository file $i on rpm distro ! Please report to dev team\n");
474 }
475 } elsif ($dtype eq "deb") {
476 if (($bn =~ /\.sources.list$/) && (not -f "/etc/apt/sources.list.d/$bn")) {
477 pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/apt/sources.list.d","Adding apt repository");
478 pb_system("sudo apt-get update","Updating apt repository");
479 } else {
480 pb_log(0,"Unable to deal with repository file $i on deb distro ! Please report to dev team\n");
481 }
482 } else {
483 pb_log(0,"Unable to deal with repository file $i on that distro ! Please report to dev team\n");
484 }
485}
486return;
487}
488
489=over 4
490
491=item B<pb_distro_get_param>
492
493This function gets the parameter in the conf file from the most precise tuple up to default
494
495=cut
496
497sub pb_distro_get_param {
498
499my $param = "";
500my $ddir = shift;
501my $dver = shift;
502my $darch = shift;
503my $opt = shift;
504my $dfam = shift || "unknown";
505my $dtype = shift || "unknown";
506
507if (defined $opt->{"$ddir-$dver-$darch"}) {
508 $param = $opt->{"$ddir-$dver-$darch"};
509} elsif (defined $opt->{"$ddir-$dver"}) {
510 $param = $opt->{"$ddir-$dver"};
511} elsif (defined $opt->{"$ddir"}) {
512 $param = $opt->{"$ddir"};
513} elsif (defined $opt->{$dfam}) {
514 $param = $opt->{$dfam};
515} elsif (defined $opt->{$dtype}) {
516 $param = $opt->{$dtype};
517} elsif (defined $opt->{"default"}) {
518 $param = $opt->{"default"};
519} else {
520 $param = "unknown";
521}
522return($param);
523
524}
525
526
527=back
528
529=head1 WEB SITES
530
531The 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/>.
532
533=head1 USER MAILING LIST
534
535None exists for the moment.
536
537=head1 AUTHORS
538
539The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
540
541=head1 COPYRIGHT
542
543Project-Builder.org is distributed under the GPL v2.0 license
544described in the file C<COPYING> included with the distribution.
545
546=cut
547
548
5491;
Note: See TracBrowser for help on using the repository browser.