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

Last change on this file since 1064 was 1064, checked in by Bruno Cornec, 14 years ago

r3931@dhcp184-49-175-19: bruno | 2010-06-26 08:02:59 +0200

  • Adds the notion of os at the top in order to provide an even better tuple management
File size: 14.2 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_conffile 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, $dos, $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, $dos, $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, $dos, $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_conffile>
63
64This function returns the mandatory configuration file used for distribution/OS detection
65
66=cut
67
68sub pb_distro_conffile {
69
70return("CCCC/pb.conf");
71}
72
73
74=item B<pb_distro_init>
75
76This 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.
77
78As an example, Ubuntu and Debian are in the same "du" family. As well as RedHat, RHEL, CentOS, fedora are on the same "rh" family.
79Mandriva, Open SuSE and Fedora have all the same "rpm" type of build system. Ubuntu ad Debian have the same "deb" type of build system.
80And "fc" is the extension generated for all Fedora packages (Version will be added by pb).
81All these information are stored in an external configuration file typically at /etc/pb/pb.conf
82
83When 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.
84
85Cf: http://linuxmafia.com/faq/Admin/release-files.html
86Ideas taken from http://search.cpan.org/~kerberus/Linux-Distribution-0.14/lib/Linux/Distribution.pm
87
88=cut
89
90
91sub pb_distro_init {
92
93my $ddir = shift || undef;
94my $dver = shift || undef;
95my $dfam = "unknown";
96my $dtype = "unknown";
97my $dos = "unknown";
98my $dsuf = "unknown";
99my $dupd = "unknown";
100my $darch = shift || undef;
101my $dnover = "false";
102my $drmdot = "false";
103
104# Adds conf file for distribution description
105# the location of the conf file is finalyzed at install time
106# depending whether we deal with package install or tar file install
107pb_conf_add(pb_distro_conffile());
108
109# If we don't know which distribution we're on, then guess it
110($ddir,$dver) = pb_distro_get() if ((not defined $ddir) || (not defined $dver));
111
112# Initialize arch
113$darch=pb_get_arch() if (not defined $darch);
114
115my ($osfamily,$ostype,$osupd,$ossuffix,$osnover,$osremovedotinver,$os) = pb_conf_get("osfamily","ostype","osupd","ossuffix","osnover","osremovedotinver","os");
116
117# Dig into the tuple to find the best answer
118$dfam = pb_distro_get_param($ddir,$dver,$darch,$osfamily);
119$dos = pb_distro_get_param($ddir,$dver,$darch,$os);
120$dtype = $ostype->{$dfam} if (defined $ostype->{$dfam});
121$dupd = pb_distro_get_param($ddir,$dver,$darch,$osupd,$dfam,$dtype);
122$dsuf = pb_distro_get_param($ddir,$dver,$darch,$ossuffix,$dfam,$dtype);
123$dnover = pb_distro_get_param($ddir,$dver,$darch,$osnover,$dfam,$dtype);
124$drmdot = pb_distro_get_param($ddir,$dver,$darch,$osremovedotinver,$dfam,$dtype);
125
126# Some OS have no interesting version
127$dver = "nover" if ($dnover eq "true");
128
129# For some OS remove the . in version name
130$dver =~ s/\.//g if ($drmdot eq "true");
131
132if ((not defined $dsuf) || ($dsuf eq "")) {
133 # By default suffix is a concatenation of .ddir and dver
134 $dsuf = ".$ddir$dver"
135} else {
136 # concat just the version to what has been found
137 $dsuf = ".$dsuf$dver";
138}
139
140# if ($arch eq "x86_64") {
141# $opt="--exclude=*.i?86";
142# }
143pb_log(2,"DEBUG: pb_distro_init: $ddir, $dver, $dfam, $dtype, $dsuf, $dupd, $darch\n");
144
145return($ddir, $dver, $dfam, $dtype, $dos, $dsuf, $dupd, $darch);
146}
147
148=item B<pb_distro_get>
149
150This 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.
151
152On my home machine it would currently report ("mandriva","2009.0").
153
154=cut
155
156sub pb_distro_get {
157
158# 1: List of files that unambiguously indicates what distro we have
159# 2: List of files that ambiguously indicates what distro we have
160# 3: Should have the same keys as the previous one. If ambiguity, which other distributions should be checked
161# 4: Matching Rg. Expr to detect distribution and version
162my ($single_rel_files, $ambiguous_rel_files,$distro_similar,$distro_match) = pb_conf_get("osrelfile","osrelambfile","osambiguous","osrelexpr");
163
164my $release;
165my $distro;
166
167# Begin to test presence of non-ambiguous files
168# that way we reduce the choice
169my ($d,$r);
170while (($d,$r) = each %$single_rel_files) {
171 if (defined $ambiguous_rel_files->{$d}) {
172 print STDERR "The key $d is considered as both unambiguous and ambigous.\n";
173 print STDERR "Please fix your configuration file.\n"
174 }
175 if (-f "$r" && ! -l "$r") {
176 my $tmp=pb_get_content("$r");
177 # Found the only possibility.
178 # Try to get version and return
179 if (defined ($distro_match->{$d})) {
180 ($release) = $tmp =~ m/$distro_match->{$d}/m;
181 } else {
182 print STDERR "Unable to find $d version in $r\n";
183 print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
184 $release = "unknown";
185 }
186 return($d,$release);
187 }
188}
189
190# Now look at ambiguous files
191# Ubuntu before 10.04 includes a /etc/debian_version file that creates an ambiguity with debian
192# So we need to look at distros in reverse alphabetic order to treat ubuntu always first via lsb
193foreach $d (reverse keys %$ambiguous_rel_files) {
194 $r = $ambiguous_rel_files->{$d};
195 if (-f "$r" && !-l "$r") {
196 # Found one possibility.
197 # Get all distros concerned by that file
198 my $tmp=pb_get_content("$r");
199 my $found = 0;
200 my $ptr = $distro_similar->{$d};
201 pb_log(2,"amb: ".Dumper($ptr)."\n");
202 $release = "unknown";
203 foreach my $dd (split(/,/,$ptr)) {
204 pb_log(2,"check $dd\n");
205 # Try to check pattern
206 if (defined $distro_match->{$dd}) {
207 pb_log(2,"cmp: $distro_match->{$dd} - vs - $tmp\n");
208 ($release) = $tmp =~ m/$distro_match->{$dd}/m;
209 if ((defined $release) && ($release ne "unknown")) {
210 $distro = $dd;
211 $found = 1;
212 last;
213 }
214 }
215 }
216 if ($found == 0) {
217 print STDERR "Unable to find $d version in $r\n";
218 print STDERR "Please report to the maintainer bruno_at_project-builder.org\n";
219 $release = "unknown";
220 } else {
221 return($distro,$release);
222 }
223 }
224}
225return("unknown","unknown");
226}
227
228
229=item B<pb_distro_installdeps>
230
231This function install the dependencies required to build the package on an RPM based distro
232dependencies can be passed as a parameter in which case they are not computed
233
234=cut
235
236sub pb_distro_installdeps {
237
238# SPEC file
239my $f = shift || undef;
240my $dtype = shift || undef;
241my $dupd = shift || undef;
242my $deps = shift || undef;
243
244# Protection
245return if (not defined $dupd);
246
247# Get dependecies in the build file if not forced
248$deps = pb_distro_getdeps($f, $dtype) if (not defined $deps);
249pb_log(2,"deps: $deps\n");
250return if ((not defined $deps) || ($deps =~ /^\s*$/));
251if ($deps !~ /^[ ]*$/) {
252 pb_system("$dupd $deps","Installing dependencies ($deps)");
253 }
254}
255
256=item B<pb_distro_getdeps>
257
258This function computes the dependencies indicated in the build file and return them as a string of packages to install
259
260=cut
261
262sub pb_distro_getdeps {
263
264my $f = shift || undef;
265my $dtype = shift || undef;
266
267my $regexp = "";
268my $deps = "";
269my $sep = $/;
270
271# Protection
272return("") if (not defined $dtype);
273return("") if (not defined $f);
274
275pb_log(3,"entering pb_distro_getdeps: $dtype - $f\n");
276if ($dtype eq "rpm") {
277 # In RPM this could include files, but we do not handle them atm.
278 $regexp = '^BuildRequires:(.*)$';
279} elsif ($dtype eq "deb") {
280 $regexp = '^Build-Depends:(.*)$';
281} elsif ($dtype eq "ebuild") {
282 $sep = '"'.$/;
283 $regexp = '^DEPEND="(.*)"\n'
284} else {
285 # No idea
286 return("");
287}
288pb_log(2,"regexp: $regexp\n");
289
290# Preserve separator before using the one we need
291my $oldsep = $/;
292$/ = $sep;
293open(DESC,"$f") || die "Unable to open $f";
294while (<DESC>) {
295 pb_log(4,"read: $_\n");
296 next if (! /$regexp/);
297 chomp();
298 # What we found with the regexp is the list of deps.
299 pb_log(2,"found deps: $_\n");
300 s/$regexp/$1/i;
301 # Remove conditions in the middle and at the end for deb
302 s/\(\s*[><=]+.*\)[^,]*,/,/g;
303 s/\(\s*[><=]+.*$//g;
304 # Same for rpm
305 s/[><=]+[^,]*,/,/g;
306 s/[><=]+.*$//g;
307 # Improve string format (remove , and spaces at start, end and in double
308 s/,/ /g;
309 s/^\s*//;
310 s/\s*$//;
311 s/\s+/ /g;
312 $deps .= " ".$_;
313}
314close(DESC);
315$/ = $oldsep;
316pb_log(2,"now deps: $deps\n");
317my $deps2 = pb_distro_only_deps_needed($dtype,$deps);
318return($deps2);
319}
320
321
322=item B<pb_distro_only_deps_needed>
323
324This function returns only the dependencies not yet installed
325
326=cut
327
328sub pb_distro_only_deps_needed {
329
330my $dtype = shift || undef;
331my $deps = shift || undef;
332
333return("") if ((not defined $deps) || ($deps =~ /^\s*$/));
334my $deps2 = "";
335# Avoid to install what is already there
336foreach my $p (split(/ /,$deps)) {
337 if ($dtype eq "rpm") {
338 my $res = pb_system("rpm -q --whatprovides --quiet $p","","quiet");
339 next if ($res eq 0);
340 } elsif ($dtype eq "deb") {
341 my $res = pb_system("dpkg -L $p","","quiet");
342 next if ($res eq 0);
343 } elsif ($dtype eq "ebuild") {
344 } else {
345 # Not reached
346 }
347 pb_log(2,"found deps2: $p\n");
348 $deps2 .= " $p";
349}
350
351$deps2 =~ s/^\s*//;
352pb_log(2,"now deps2: $deps2\n");
353return($deps2);
354}
355
356=item B<pb_distro_setuprepo>
357
358This function sets up potential additional repository to the build environment
359
360=cut
361
362sub pb_distro_setuprepo {
363
364my $ddir = shift || undef;
365my $dver = shift;
366my $darch = shift;
367my $dtype = shift || undef;
368
369my ($addrepo) = pb_conf_read("$ENV{'PBDESTDIR'}/pbrc","addrepo");
370return if (not defined $addrepo);
371
372my $param = pb_distro_get_param($ddir,$dver,$darch,$addrepo);
373return if ($param eq "");
374
375# Loop on the list of additional repo
376foreach my $i (split(/,/,$param)) {
377
378 my ($scheme, $account, $host, $port, $path) = pb_get_uri($i);
379 my $bn = basename($i);
380
381 # The repo file can be local or remote. download or copy at the right place
382 if (($scheme eq "ftp") || ($scheme eq "http")) {
383 pb_system("wget -O $ENV{'PBTMP'}/$bn $i","Donwloading additional repository file $i");
384 } else {
385 copy($i,$ENV{'PBTMP'}/$bn);
386 }
387
388 # The repo file can be a real file or a package
389 if ($dtype eq "rpm") {
390 if ($bn =~ /\.rpm$/) {
391 my $pn = $bn;
392 $pn =~ s/\.rpm//;
393 if (pb_system("rpm -q --quiet $pn","","quiet") != 0) {
394 pb_system("sudo rpm -Uvh $ENV{'PBTMP'}/$bn","Adding package to setup repository");
395 }
396 } elsif ($bn =~ /\.repo$/) {
397 # Yum repo
398 pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/yum.repos.d","Adding yum repository") if (not -f "/etc/yum.repos.d/$bn");
399 } elsif ($bn =~ /\.addmedia/) {
400 # URPMI repo
401 # We should test that it's not already a urpmi repo
402 pb_system("chmod 755 $ENV{'PBTMP'}/$bn ; sudo $ENV{'PBTMP'}/$bn 2>&1 > /dev/null","Adding urpmi repository");
403 } else {
404 pb_log(0,"Unable to deal with repository file $i on rpm distro ! Please report to dev team\n");
405 }
406 } elsif ($dtype eq "deb") {
407 if (($bn =~ /\.sources.list$/) && (not -f "/etc/apt/sources.list.d/$bn")) {
408 pb_system("sudo mv $ENV{'PBTMP'}/$bn /etc/apt/sources.list.d","Adding apt repository");
409 pb_system("sudo apt-get update","Updating apt repository");
410 } else {
411 pb_log(0,"Unable to deal with repository file $i on deb distro ! Please report to dev team\n");
412 }
413 } else {
414 pb_log(0,"Unable to deal with repository file $i on that distro ! Please report to dev team\n");
415 }
416}
417return;
418}
419
420=item B<pb_distro_get_param>
421
422This function gets the parameter in the conf file from the most precise tuple up to default
423
424=cut
425
426sub pb_distro_get_param {
427
428my $param = "";
429my $ddir = shift;
430my $dver = shift;
431my $darch = shift;
432my $opt = shift;
433my $dfam = shift || "unknown";
434my $dtype = shift || "unknown";
435my $dos = shift || "unknown";
436
437if (defined $opt->{"$ddir-$dver-$darch"}) {
438 $param = $opt->{"$ddir-$dver-$darch"};
439} elsif (defined $opt->{"$ddir-$dver"}) {
440 $param = $opt->{"$ddir-$dver"};
441} elsif (defined $opt->{"$ddir"}) {
442 $param = $opt->{"$ddir"};
443} elsif (defined $opt->{$dfam}) {
444 $param = $opt->{$dfam};
445} elsif (defined $opt->{$dtype}) {
446 $param = $opt->{$dtype};
447} elsif (defined $opt->{$dos}) {
448 $param = $opt->{$dos};
449} elsif (defined $opt->{"default"}) {
450 $param = $opt->{"default"};
451} else {
452 $param = "";
453}
454
455# Allow replacement of variables inside the parameter such as ddir, dver, darch for rpmbootstrap
456# but not shell variable which are backslashed
457if ($param =~ /[^\\]\$/) {
458 pb_log(3,"Expanding variable on $param\n");
459 eval { $param =~ s/(\$\w+)/$1/eeg };
460}
461
462pb_log(2,"DEBUG: pb_distro_get_param on $ddir-$dver-$darch returns ==$param==\n");
463return($param);
464
465}
466
467
468=back
469
470=head1 WEB SITES
471
472The 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/>.
473
474=head1 USER MAILING LIST
475
476None exists for the moment.
477
478=head1 AUTHORS
479
480The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
481
482=head1 COPYRIGHT
483
484Project-Builder.org is distributed under the GPL v2.0 license
485described in the file C<COPYING> included with the distribution.
486
487=cut
488
489
4901;
Note: See TracBrowser for help on using the repository browser.