source: soft/build_system/build_system/iurt/trunk/uiurt @ 1

Last change on this file since 1 was 1, checked in by fasma, 12 years ago

Initial Import from Mandriva's soft revision 224062 and package revision 45733

File size: 52.1 KB
Line 
1#!/usr/bin/perl
2#
3# Copyright (C) 2005 Mandrakesoft
4# Copyright (C) 2005,2006 Mandriva
5#
6# Author: Florent Villard <warly@mandriva.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2, or (at your option)
11# any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21#
22# NAME
23#
24# uiurt - micro iurt
25#
26# DESCRIPTION
27#
28# Minimal, strict, functionality to build packages in a chroot.
29#
30# Does not try to be smart and resolve host environment problems, but just
31# fail and report insted.
32#
33# TODO
34#
35# - use a cache (rpmctl cache for example) to find maintainer
36# - add icecream compilation support
37# - add a --group option to compile a set of packages (in progress)
38# - add a function to update a packages when it obviously need to be recompile
39# - Maybe call the function from the initial todo list (thus making the
40#   argument ordering important)
41# - Change the packager tag in the chroot to have the one who submit the package
42
43use strict;
44use RPM4::Header;
45use Iurt::Config qw(config_usage get_date get_prefix config_init dump_cache_par get_maint get_date check_arch %arch_comp get_package_prefix);
46use Data::Dumper;
47use URPM;
48use Iurt::DKMS;
49use Iurt::Urpmi;
50use Iurt::Chroot qw(add_local_user create_temp_chroot remove_chroot clean_unionfs clean_all_unionfs clean_all_chroot_tmp check_build_chroot clean_chroot);
51use Iurt::Process qw(perform_command clean kill_for_good sudo);
52use Iurt::Mail qw(sendmail);
53use Iurt::Util qw(plog_init plog);
54use File::NCopy qw(copy);
55use File::Path qw(mkpath);
56use File::Spec::Functions qw(rel2abs);
57use File::Basename qw(fileparse);
58# I did not manage to make locks work over the network
59#use File::lockf;
60use Mkcd::Commandline qw(parseCommandLine usage);
61use MDK::Common;
62use Filesys::Df qw(df);
63
64my $program_name = 'iurt2';
65my $VERSION = '0.6.2';
66# sessing parameters
67my $sudo = '/usr/bin/sudo';
68my $arg = @ARGV;
69my (@params, %run);
70$run{program_name} = $program_name;
71       
72$run{todo} = [];
73@params = ( 
74    #    [ "one letter option", "long name option", "number of args (-X means Žat least XŽ)", "help text", "function to call", "log info"]
75    #
76    # no_rsync, config_help and copy_srpm kept for compatibility reasons
77    #
78    [ "", $program_name, 0, "[--cache] [--chrooted-urpmi <media prefix>] [--concurrent-run] [--config foo value] [--warn] [--verbose integer]
79            [--copy-srpm] [--debug] [--distro] [--no-rsync] [--clean user1 user2 user3] [--clean-all] [--shell] [--stop {p|c|i|l|b|a|s}]
80            [--use-system-distrib] [--dir] [--help foo?] [--log filename] [--group] [--unionfs]
81            [--upload [--markrelease] [--source]] [--dir] [--help foo?] [--log filename]  [--unionfs] [--status] [--ignore-failure]
82            [--repository <distribution path>]
83            {--config_help | --dkms {--media <media regexp>}
84            --chroot --arch {i586|x86_64|ppc} --distro {cooker|2006.0|community/2006.0|...} } |
85            --rebuild {cooker|2006.0|community/2006.0|...} {i586|x86_64|ppc|...} {filename1.src.rpm} {filename2.src.rpm} ... {filenamen.src.rpm} }", 
86    "$program_name is a perl script to rebuild automatically several rpm in chroot, given a sourcerpm repository, and mail authors or rebuilder when problems occurs.
87
88                e.g.: iurt --repository /dis/ -p foo\@foo.net -r cooker x86_64  /SRPMS/main/release/mkcd-4.2.5-1mdv2007.1.src.rpm", 
89    sub { $arg or usage($program_name, \@params) }, "" ],
90    [ "", "distro", 1, "<distro>", 
91    "Set the distribution",
92    sub { ($run{distro}) = @_; 1 }, "Setting the distribution" ],
93    [ "", "dkms", [                                                                                                                                     
94        ["", "dkms", 0, "",                                 
95        "Set the DKMS rebuild mode",
96        sub {   
97            my ($tmp, @arg) = @_; 
98            $tmp->[0] ||= {}; 
99            push @$tmp, @arg; 
100            1;
101        }, "Setting auto mode arguments"], 
102        ["k", "kmedia", 1, "<kernel media regexp>", 
103        "Media Regexp to limit the kernel search to", 
104        sub { my ($tmp, $kmedia) = @_; $tmp->[0]{kmedia} = $kmedia; 1 }, "Limiting rebuild to the kernel in the given media regexp"], 
105        ["m", "media", 1, "<media regexp>", 
106        "Media Regexp to limit rebuild to", 
107        sub { my ($tmp, $media) = @_; $tmp->[0]{media} = $media; 1 }, "Limiting rebuild to the given media regexp"], 
108], "[options]", 
109    "Set the DKMS rebuild mode",
110    sub { my ($opt) = @_; $run{dkms} = $opt; 1 }, "Running a DKMS rebuild run" ],
111    [ "a", "arch", 1, "<architecture>", 
112    "Set the architecture",
113    sub { ($run{my_arch}) = @_; 1 }, "Setting architecture" ],
114    [ "", "cache", 0, "", 
115    "Use the global cache file",
116    sub { $run{use_cache} = 1 }, "Activating cache use" ],
117    [ "", "copy-srpm", 0, "", 
118    "Copy also the regenerated SRPM",
119    sub { $run{copy_srpm} = 1 }, "Activating the copy_srpm mode" ],
120    [ "", "copy_srpm", 0, "", 
121    "Copy also the regenerated SRPM",
122    sub { $run{copy_srpm} = 1 }, "Activating the copy_srpm mode" ],
123    [ "c", "chroot", 0, "", 
124    "Check chroot and update it if needed",
125    sub { $run{chroot} = 1 }, "Activating chroot updating" ],
126    [ "", "chrooted-urpmi", [
127        [ "", "chrooted-urpmi", 1, "",
128        "Create urpmi media inside the chroot instead of using --root (media prefix is like http:///server.mandriva.com/dis/)",
129        sub {
130            my ($tmp, @arg) = @_; 
131            $tmp->[0] ||= {}; 
132            push @$tmp, @arg; 
133            1;
134        }, "Setting chrooted-urpmi options" ],
135        ["m", "media", -1, "<media1> <media2> ... <median>", 
136        "Media to add instead of --distrib", 
137        sub { my ($tmp, @media) = @_; $tmp->[0]{media} = \@media; 1 }, "Limiting rebuild to the kernel in the given media regexp"], 
138    ] , "[options] <media prefix>", 
139    "Create urpmi media inside the chroot instead of using --root (media prefix is like http:///server.mandriva.com/dis/)",
140    sub { my ($opt, $media) = @_; $opt->{rooted_media} = $media; $run{chrooted_urpmi} = $opt; 1 }, "Activating chroot media" ],
141    [ "", "clean-all", 0, "", 
142    "Clean all remaining chroots for all the users",
143    sub { $run{clean_all} = 1 }, "Activating clean chroot flag" ],
144    [ "", "clean", -1, "<user 1> <user 2> ... <user n>", 
145    "Clean remaining chroot before runing",
146    sub { $run{clean} = \@_ }, "Activating clean chroot flag" ],
147    [ "", "concurrent-run", 0, "", 
148    "Allow several iurt to run on different machines (slower)",
149    sub { $run{concurrent_run} = 1 }, "Activating concurrent run checks" ],
150    [ "d", "dir", -1, "", 
151    "Directory where to find packages to rebuild", 
152    sub { $run{extra_dir} = \@_; 1 }, "Adding extra source packages directories" ],
153    [ "", "config", 2, "<configuration keyword> <value>", 
154    "Override a configuration file variable",
155    sub { my ($key, $value) = @_; $run{config}{$key} = $value }, "Overriding configuration variable" ],
156    [ "", "config-help", 0, "", 
157    "Explain configuration files keywords", 
158    sub { $run{config_usage} = 1 }, "Activating debug mode" ],
159    [ "", "config_help", 0, "", 
160    "Explain configuration files keywords", 
161    sub { $run{config_usage} = 1 }, "Activating debug mode" ],
162    [ "", "debug", 0, "", 
163    "Activate debug mode", 
164    sub { $run{debug} = 1 }, "Activating debug mode" ],
165    [ "g", "group", 0, "", 
166    "Activate group mode, packages will be compiled as a global set, not as individual packages", 
167    sub { $run{group} = 1 }, "Activating the group mode" ],
168    [ "", "ignore-failure", 0, "", 
169    "Do not take into account the failure cache, try to recompile all the packages not synchronized", 
170    sub { $run{ignore_failure} = 1 }, "Activating the mode ignoring previous failure" ],
171    [ "u", "unionfs", 0, "", 
172    "Activate unionfs mode", 
173    sub { $run{unionfs} = 1 }, "Activating unionfs mode" ],
174    [ "l", "log", 1, "<log file>", 
175    "Log file.", 
176    sub { 
177        $run{log} = pop @_; 
178        open my $log, ">$run{log}" or die "unable to open $run{log}\n";
179     $run{LOG} = sub { print $log @_ };
180        print *$log, "command line: @ARGV\n";
181        1;
182    }, "Log file" ],
183    [ "m", "media", -1, "<media 1> <media 2> ... <media 3>", 
184    "Media to rebuild", 
185    sub { ($run{media}) = @_; 1 }, "Adding a media to rebuild" ],
186    [ "n", "no", 0, "", 
187    "Perform all the check but do not compile anything", 
188    sub { ($run{no_compile}) = 1 }, "Setting the no compilation flag" ],
189    [ "p", "packager", 1, "<packager>", 
190    "Use a specific packager",
191    sub { ($run{packager}) = @_ }, 'Setting packager tag'], 
192    [ "r", "rebuild", -2, "<distro> <architecture> <srpm 1> <srpm 2> ... <srpm n>", 
193    "Rebuild the packages, e.g. $program_name -r cooker x86_64 /home/foo/rpm/SRPMS/foo-2.3-12mdv2007.0.src.rpm", 
194    sub { 
195        $run{rebuild} = 1; 
196        $run{distro} = shift @_;
197        $run{my_arch} = shift @_;
198
199        foreach (@_) {
200            my ($path, $srpm);
201
202            unless (-f $_ && -r $_) {
203                die "FATAL $program_name: $_ not a file or cannot be read\n";
204            }
205
206            ($srpm, $path) = fileparse(rel2abs($_));
207            ($srpm =~ /\.src\.rpm$/) || die "FATAL: $_ doesn't look like an SRPM";
208
209            if (check_arch($_, $run{my_arch})) {
210                plog('DEBUG', "force build for $2 (from $1)");
211                push @{$run{todo}}, [ $path, $srpm, 1 ];
212            } else {
213                plog("ERROR: $_ could not be build on $run{my_arch}, ignored.");
214            }
215        }
216        1;
217        }, "Activating rebuild mode" ],
218    [ "", "upload", [
219        ["", "upload", 0, "[options]", 
220        "Upload the rebuild packages", 
221        sub {   my ($tmp) = @_;
222            $tmp->[0] ||= {};
223            1;
224        }, "Setting upload options"],
225        [ "m", "markrelease", 0, "", 
226        "Mark SVN directory when uploading the packages", 
227        sub { $run{markrelease} = 1 }, "Adding markrelease repsys option" ],
228        [ "s", "source", 0, "", 
229        "Upload the source package as wells", 
230        sub { $run{source_upload} = 1 }, "Setting source flag for upload" ],
231    ], "[options]", 
232    "Upload the rebuild packages", 
233    sub { $run{upload} = 1 }, "Setting the upload flag" ],
234    [ "", "use-old-chroot", 1, "<chroot path>", 
235    "Use the given chroot as chroot (usefull for debugging)", 
236    sub { ($run{use_old_chroot}) = @_ }, "Using given chroot" ],
237    [ "", "no_rsync", 0, "", 
238    "Do not send build log to the distant rsync server", 
239    sub { $run{no_rsync} = 1 }, "Setting the no rsync warn flag" ],
240    [ "", "no-rsync", 0, "", 
241    "Do not send build log to the distant rsync server", 
242    sub { $run{no_rsync} = 1 }, "Setting the no rsync warn flag" ],
243    [ "", "use-system-distrib", 1, "<media>", 
244    "Use the current system urpmi configuration", 
245    sub { $run{use_system_distrib} = shift; 1 }, "Setting system distrib for urpmi configuration" ],
246    [ "v", "verbose", 1, "<verbose level>", 
247    "Give more info messages about what is going on (level from 1 to 10)", 
248    sub { $run{verbose} = $_[0]; 1 }, "Setting verbose level" ],
249    [ "w", "warn", 0, "", 
250    "Warn maintainer of the packages about problem in the rebuild", 
251    sub { $run{warn} = 1; 1 }, "Setting warn flag to warn maintainers" ],
252    [ "", "shell", 0, "", 
253    "Dump to a shell into the newly created chroot with sudo on rpm, urpmi, urpme and urpmi.addmedia", 
254    sub { 
255        ($run{shell}) = 1; 
256        1 }, "Setting option to dump to a shell" ],
257    [ "", "stop", 1, "<rpm step>", 
258    "Perform rpm -b<rpm step> (p c i l b a s) instead of rpm -ba and then open a shell in the chroot", 
259    sub { 
260        ($run{stop}) = @_; 
261        1;
262    }, "Setting rpm build option" ],
263    [ "", "repository", 1, "<distribution root path>",
264    "Set a repository path if one is not created in the configuration file",
265    sub {
266        ($run{repository}) = @_;
267        1;
268    } , "Setting the repository" ],
269    [ "", "status", 1, "<mail>", 
270    "Send a status mail to the provided mail address", 
271    sub { 
272        ($run{status_mail}) = @_; 
273        1;
274    }, "Setting status mail option" ],
275    [ "", "with", 1, "<flag>",
276    "Use specified --with flag with rpm (can be used multiple times)",
277    sub {
278        ($run{with_flags}) = $run{with_flags} . " --with " . @_[0];
279        1;
280    }, "Adding specified extra --with parameter to rpm" ],
281    [ "", "without", 1, "<flag>",
282    "Use specified --without flag with rpm (can be used multiple times)",
283    sub {
284        ($run{with_flags}) = $run{with_flags} . " --without " . @_[0];
285        1;
286    }, "Adding specified extra --without parameter to rpm" ],
287);
288
289open(my $LOG, ">&STDERR");
290$run{LOG} = sub { print $LOG @_ };
291
292plog_init($program_name, $LOG, $run{verbose}, 1);
293#plog_init($program_name, $LOG, 7, 1); # CM: hardcoded for now, will fix ASAP
294
295
296# Display version information
297#
298(my $iurt_rev = '$Rev: 145712 $') =~ s/.*: (\d+).*/$1/;
299(my $iurt_aut = '$Author: blino $') =~ s/.*: (..).*/$1/;
300(my $iurt_dat = '$Date: 2007-03-18 09:25:20 -0300 (Sun, 18 Mar 2007) $')
301        =~ s/.*: ([\d-]* [\d:]*) .*/$1/;
302plog("MSG", "This is iurt2 revision $iurt_rev-$iurt_aut ($iurt_dat)");
303
304
305my $todo = parseCommandLine($program_name, \@ARGV, \@params);
306@ARGV and usage($program_name, \@params, "@ARGV, too many arguments");
307foreach my $t (@$todo)  {
308    plog('DEBUG', $t->[2]);
309    &{$t->[0]}(@{$t->[1]}) or plog('ERR', $t->[2]);
310}
311
312$run{distro_tag} = $run{distro};
313$run{distro_tag} =~ s,/,-,g;
314
315my $real_arch = `uname -m`;
316chomp $real_arch;
317my $HOME = $ENV{HOME};
318my $configfile = "$HOME/.iurt.$run{distro_tag}.conf";
319
320plog('DEBUG', "load config: $configfile");
321my $config;
322if (-f $configfile) {
323    $config = eval(cat_($configfile))
324        or die "FATAL $program_name: syntax error in $configfile";
325} else {
326    $config = {};
327}
328
329if ($run{repository}) {
330    plog('DEBUG', "overriding configuration repository by the one given in the command line");
331    $config->{repository} = $run{repository}
332}
333
334if (!$config->{repository}) {
335    die "FATAL $program_name: no repository have been defined (use --repository to specify one on the command line"
336}
337
338my $urpmi = Iurt::Urpmi->new(run => \%run, config => $config, urpmi_options => "-v --no-verify-rpm --nolock --auto --ignoresize");
339$run{urpmi} = $urpmi;
340
341if (!$run{chrooted_urpmi} && $run{group}) {
342    die "FATAL $program_name: option --chrooted-urpmi is mandatory if --group is selected";
343} 
344
345my %config_usage = ( 
346    admin => {
347        desc => 'Mail of the administrator of packages builds',
348        default => ''
349    },
350    all_media => {
351        desc => 'List of known media',
352        default => {
353            'main' => [ '' ],
354            'contrib' => [ '' ]
355        }
356    },
357    basesystem_media_root => {
358        desc => 'Name of the media holding basesystem packages',
359        default => sub {
360            my ($config, $run) = @_;
361            "$config->{repository}/$run->{distro}/$run->{my_arch}/";
362        }
363    },
364    basesystem_media => {
365        desc => 'Where to find basesystem packages',
366        default => 'main/release'
367    },
368    basesystem_packages => {
369        desc => 'List of packages needed for the chroot creation',
370        default => [
371            'basesystem',
372            'rpm-build',
373            'rpm-mandriva-setup-build',
374            'sudo',
375            'urpmi',
376            'curl',
377        ]
378    },
379    build_timeout => {
380        desc => 'Maximum build time after which the build process is terminated',
381        default => {
382            default => 18000,
383        },
384    },
385    cache_home => {
386        desc => 'Where to store the cache files',
387        default => "$HOME/.bugs"
388    },
389    cache_min_size => {
390        desc => 'Minimal size to consider a cache file valid',
391        default => 1000000
392    },
393    check_binary_file => {
394        desc => 'Packages rebuild should be checked, however sometime rpm is segfaulting and the test is not correct',
395        default => 0
396    },
397    iurt_root_command => {
398        desc => 'Program to run sudo command',
399        default => '/usr/sbin/iurt_root_command'
400    },
401    distribution => {
402        desc => 'Name of the packages distribution',
403        default => 'Mandriva Linux'
404    },
405    home => {
406        desc => 'Home dir',
407        default => $HOME
408    },
409    install_chroot_binary => {
410        desc => 'Tool used to create initial chroot',
411        default => 'install-chroot-tar.sh'
412    },
413    local_home => {
414        desc => 'Where to build packages',
415        default => $HOME
416    },
417    local_upload => {
418        desc => 'Where to store build packages and log',
419        default => ''
420    },
421    local_spool => {
422        desc => 'To override the directory where all the results are stored',
423        default => ''
424    },
425    log_size_limit => {
426        desc => 'Maximum authorized size for a log file',
427        default => '100M'
428    },
429    log_size_date => {
430        desc => 'Number of days log should be kept',
431        default => '30'
432    },
433    log_url => {
434        desc => 'Where the log can be seen',
435        default => ''
436    },
437    minimum_package_number => {
438        "Minimum number of packages in a synthesis file to consider it valid",
439        default => 1000
440    },
441    max_command_retry => {
442        "Maximum number of retry Iurt will perform for a given command",
443        default => 20
444    },
445    no_mail  => {
446        desc => 'Hash table with people mail address where we should not send any mails',
447        default => {}
448    },
449    packager => {
450        desc => 'Name of the build bot',
451        default => 'Iurt'
452    },
453    prompt => { 
454        desc => 'Default prompt in the chroot',
455        default => qq{PS1='[\\033[00;33m\\]iurt $run{distro}\\[\\033[00m\\]] \\[\\033[00;31m\\]\\u\\[\\033[00;32m\\]\\h\\[\\033[00m\\]\\w\$ '},
456    },
457    repository => {
458        desc => 'Prefix of the repositories',
459        default => ''
460    },
461    rsync_to => {
462        desc => 'Server where the result of the builds should be rsynced (name@server:path format)',
463        default => ''
464    },
465    sendmail => {
466        desc => 'If the bot will send mail reports regarding build',
467        default => 0
468    },
469    supported_arch => {
470        desc => 'Table of supported architecture',
471        default => ['i586', 'x86_64']
472    },
473    upload => {
474        desc => 'Where to copy build packages',
475        default => "$HOME/uploads/"
476    },
477    vendor => {
478        desc => 'Name of the packages vendor',
479        default => 'Mandriva'
480    },
481);
482
483config_usage() if $run{config_usage};
484$run{my_arch} or usage($program_name, \@params, "no architecture given (media $run{media}, run{my_arch} $run{my_arch}, todo", join(', ', @{$run{todo}}));
485if (!$arch_comp{$real_arch}{$run{my_arch}}) {
486    die "FATAL $program_name: could not compile $run{my_arch} binaries on a $real_arch";
487}
488config_init(\%config_usage, $config, \%run);
489
490$config->{upload} .= $run{distro};
491$config->{upload} =~ s/community//g;
492if ($run{distro} ne 'cooker') {
493    if ($run{media} ne 'main') {
494        $config->{upload} .= "/$run{media}";
495    }
496} elsif ($run{media} eq 'contrib') {
497    $config->{upload} =~ s/cooker/contrib/g;
498}
499
500my $lock = $run{media};
501my $local; # FIXME: (tv) variable $local assigned, but not read
502if (!$lock && $run{chroot}) {
503    $lock = 'chroot';
504    $local = 1;
505}
506if (!$lock && $run{dkms}) {
507    $lock = 'dkms';
508    $local = 0;
509}
510$run{lock} = $lock;
511
512# cache file name is needed early to remove the manual lock file if the
513# lock mechanism does not work
514
515mkpath $config->{cache_home};
516my $cachefile = "$config->{cache_home}/iurt.$run{distro_tag}.$run{my_arch}.$lock.cache";
517$run{cachefile} = $cachefile;
518if (!$run{debug} && $run{media} || $run{chroot}) {
519    $run{pidfile_home} = "$config->{cache_home}/";
520    $run{pidfile} = "iurt.$run{distro_tag}.$run{my_arch}.$lock";
521    check_pid(\%run);
522}
523
524$config->{local_upload} ||= $config->{local_home};
525my $local_spool;
526if ($config->{local_spool}) {
527    $local_spool = $config->{local_spool};
528} else {
529    $local_spool = "$config->{local_upload}/iurt/$run{distro_tag}/$run{my_arch}/$run{media}/";
530}
531
532# Squash double slashes
533$local_spool =~ y!/!!s;
534
535plog('INFO', "local spool: $local_spool");
536if (!-d "$local_spool/log") {
537    plog('DEBUG', "creating local spool $local_spool");
538    mkpath("$local_spool/log")
539        or die "FATAL: could not create local spool dir $local_spool ($!)";
540}
541$run{local_spool} = $local_spool;
542
543my $cache;
544my $clear_cache = 1;
545if (-f $cachefile && $run{use_cache}) {
546    plog('INFO', "loading cache file $cachefile");
547
548    $cache = eval(cat_($cachefile))
549        or plog('ERR', "FATAL: could not load cache $cachefile ($!)");
550
551    if (!$cache) {
552        opendir my $cache_dir, $config->{cache_home};
553        my $to_load;
554
555        foreach my $file (readdir $cache_dir) {
556            (my $date) = $file =~ /iurt\.$run{distro_tag}\.$run{my_arch}\.$run{media}\.cache\.tmp\.(\d{8})/ or next;
557            if ($date > $to_load && -s "$config->{cache_home}/$file" > $config->{cache_min_size}) {
558                $to_load = $date;
559                $cachefile = "$config->{cache_home}/$file";
560            }
561        }
562
563        plog('NOTIFY', "loading alternate cache file $cachefile");
564        $cache = eval(cat_($cachefile))
565                or plog('ERR', "FATAL: could not load cache $cachefile ($!)");
566    }
567    $clear_cache = 0 if $cache;
568}
569
570if ($clear_cache) {
571    $cache = {
572        rpm_srpm => {},
573        failure => {},
574        queue => {},
575        warning => {},
576        run => 1,
577        needed => {},
578        no_unionfs => {}
579    };
580}
581$run{cache} = $cache;
582
583my (%srpm_version, @wrong_rpm, %provides, %pack_provide, $to_compile, %maint);
584$to_compile = @{$run{todo}};
585$to_compile += check_media(\%run, $cache, $config, \%srpm_version,
586        \@wrong_rpm, \%provides, \%pack_provide, \%maint) if $run{media};
587$to_compile += search_packages(1, $cache, \%provides, \%run, \%maint,
588        \%srpm_version, @{$run{extra_dir}}) if $run{extra};
589
590my $dkms;
591if ($run{dkms}) {
592    $dkms = Iurt::DKMS->new(run => \%run, config => $config);
593    $to_compile += $dkms->search_dkms;
594}
595$run{to_compile} = $to_compile;
596
597dump_cache_par(\%run);
598
599plog("Packages to build: $to_compile");
600
601my ($fulldate, $daydate) = get_date();
602if ($run{use_cache}) {
603    $run{run} = $cache->{run};
604    $run{run} ||= 1;
605    $cache->{run} = $run{run} + 1;
606} else {
607    $run{run} = "0.$fulldate";
608}
609$run{daydate} = $daydate;
610plog('DEBUG', "using $run{run} as chroot extension");
611$run{user} = $ENV{SUDO_USER} || $ENV{USER};
612$run{uid} = getpwnam $run{user};
613
614plog('DEBUG', "using local user $run{user}, id $run{uid}");
615my $luser = $run{user} || 'builder';
616
617check_sudo_access()
618    or die "FATAL: you need to have sudo access to run $program_name";
619
620my $debug_tag = $run{debug} && '_debug';
621$run{debug_tag} = $debug_tag;
622if ($run{unionfs} && !$run{use_old_chroot}) {
623    plog(1, "adding unionfs module");
624    sudo(\%run, $config, "--modprobe", "unionfs") or $run{unionfs} = 0;
625    if ($run{unionfs}) {
626        $run{unionfs_dir} = "$config->{local_home}/iurt_unionfs$debug_tag/";
627        remove_chroot(\%run, $run{unionfs_dir}, \&clean_all_unionfs);
628        $run{unionfs_dir} = "$run{unionfs_dir}/$run{user}/";
629        -d $run{unionfs_dir} or mkdir $run{unionfs_dir};
630    }
631}
632
633my (%done, $done);
634$run{done} = \%done;
635my $home = $config->{local_home};
636my $union_id = 1;
637$run{unionfs_tmp} = $run{unionfs};     
638
639my ($chroot_name, $chroot_tmp, $chroot, $chroot_tar);
640$chroot_name = "chroot_$run{distro_tag}$debug_tag";
641if (!$run{use_old_chroot}) {
642    $chroot_tmp = "$config->{local_home}/chroot_tmp";
643
644    if (!-d $chroot_tmp) {
645        mkdir $chroot_tmp;
646    } else {
647        remove_chroot(\%run, $chroot_tmp, \&clean_all_chroot_tmp, $chroot_name);
648    }
649
650    $chroot_tmp = "$config->{local_home}/chroot_tmp/$run{user}";
651    if (!-d $chroot_tmp) {
652        mkdir $chroot_tmp;
653    }
654    $chroot_tmp = "$config->{local_home}/chroot_tmp/$run{user}/$chroot_name.$run{run}";
655    $run{chroot_tmp} = $chroot_tmp;
656
657    $chroot = "$config->{local_home}/$chroot_name";
658} else {
659    plog(1, "using given chroot $run{use_old_chroot}");
660    $chroot_tmp = $run{use_old_chroot};
661    $chroot = $run{use_old_chroot};
662}
663$run{chroot_path} = $chroot;
664$chroot_tar = "$config->{local_home}/$chroot_name.$run{my_arch}.tar.gz";
665$run{chroot_tar} = $chroot_tar;
666# 20061222 warly
667# even in use_old_chroot mode we create the chroot if it does not exist (useful
668# if the option is used for the first time
669if ($run{chroot} || !-d "$chroot/dev") {
670    check_build_chroot($chroot, $chroot_tar, \%run, $config) or die "FATAL $program_name: could not prepare initial chroot";
671}
672
673# now exit if there is nothing to do and it was just a cleaning pass
674if ($run{no_compile} || !@{$run{todo}} && !$run{debug} && !$run{shell} && !$run{rebuild}) {
675    send_status_mail(\%run, $config, $cache) if $run{status_mail};
676    plog("no package to compile :(");
677    unlink "$run{pidfile_home}/$run{pidfile}" if $run{pidfile};
678    exit();
679}
680
681plog('DEBUG', "running with pid $$");
682$run{prefix} = get_prefix($luser); 
683
684my $df = df $home;
685if ($df->{per} >= 99) {
686    die "FATAL: not enough space on the filesystem, only $df->{bavail} KB on $home, full at $df->{per}%";
687}
688
689if ($run{shell}) {
690    if (!$run{use_old_chroot}) {
691        ($union_id, my $chroot_tmp) = create_temp_chroot(\%run, $config,
692        $cache, $union_id, $chroot_tmp, $chroot_tar)
693            or die "FATAL $program_name: could not create temporary chroot";
694    }
695    add_local_user($chroot_tmp, \%run, $config, $luser, $run{uid}) or die "FATAL $program_name: could not add local user";
696
697    #$urpmi->set_command($chroot_tmp);
698    $urpmi->urpmi_command($chroot_tmp, $luser);
699
700    $urpmi->install_packages('chroot', $chroot_tmp, $local_spool, \%pack_provide, 'configure', "[ADMIN] installation of urpmi and sudo failed in the chroot $run{my_arch}", { check => 1, maintainer => $config->{admin} }, 'urpmi', 'sudo') or die "FATAL $program_name: could not add urpmi and sudo in the chroot";
701    add_sudoers(\%run, $chroot_tmp, $luser);
702    if ($run{shell}) {
703        plog('NOTIFY', "dumping to a chrooted shell into $chroot_tmp");
704        exec $sudo, 'chroot', $chroot_tmp, '/bin/su', '-', $luser, '-c', "$config->{prompt} bash";
705        die "FATAL $program_name: could not exec chroot to $chroot_tmp ($!)";
706    }
707}
708
709# perform some cleaning before running to have some more space, rsync to
710# the server too in case previous iurt crashed
711
712if ($config->{rsync_to} && !$run{no_rsync}) { 
713        # remove some old and very big log files not to saturate the server
714        system(qq(find $local_spool/log/ -name "*.log" \\( -size +$config->{log_size_limit} -or -mtime +$config->{log_size_date} \\) -exec rm -f {} \\;));
715        system('rsync', '--delete', '-alHPe', 'ssh -xc arcfour', "$local_spool/log/", "$config->{rsync_to}/$run{distro_tag}/$run{my_arch}/$run{media}/log/");
716}
717
718if ($run{dkms} && $run{dkms_todo}) {
719    $done += $dkms->dkms_compile($local_spool, $done);
720}
721
722# The next loop should be moved in a module someday
723
724# FIXME: (tv) kill this dead code or use it!!
725my $_s = sub { 
726    if ($run{main}) {
727        plog("dumping cache..."); 
728        dump_cache_par(\%run);
729        $Data::Dumper::Indent = 0;
730        $Data::Dumper::Terse = 1;
731        plog("Running environment:\n", Data::Dumper->Dump([\%run]), "\n");
732        plog("Configuration:\n", Data::Dumper->Dump([$config]), "\n");
733    }
734    exit();
735};
736#$SIG{TERM} = $s;
737#$SIG{INT} = $s;
738$run{main} = 1;
739
740my $rebuild;
741$run{group} = 0 if @{$run{todo}} == 1;
742if ($run{group}) { 
743    $rebuild = 1;
744    $urpmi->set_local_media($local_spool);
745    $urpmi->order_packages($union_id, \%provides, $luser)
746        or die "FATAL $program_name: could not order packages";
747}
748#
749# The build loop
750#
751my $prev_done = $done;
752do { 
753    $rebuild = 0;
754    $done = $prev_done;
755    for (my $i; $i < @{$run{todo}}; $i++) {
756        my ($dir, $srpm, $status) = @{$run{todo}[$i]};
757       
758        # CM: Set argv[0] (in the C sense) to something we can easily spot and
759        #     understand in process list
760        $0 = "Iurt: $run{distro_tag} $run{my_arch} $run{media} $srpm";
761
762        $status or next;
763        $done{$srpm} and next;
764        $done{$srpm} = 1;
765        check_version($srpm, \%srpm_version) or next;
766        if ($run{debug}) { $run{debug}++ == 2 and exit() }
767        $done++;
768        plog('NOTIFY', "Build package $srpm [$done/$to_compile]");
769        # FIXME unfortunately urpmi stalls quite often
770        my $retry = 0;
771         
772        # current rpm is sometime segfaulting, and iurt is them blocked
773        # and cannot
774        #
775        # $cache->{failure}{$srpm} = 1;
776        # dump_cache(\%run);
777retry:
778        $urpmi->clean_urpmi_process;
779       
780        if (!$run{use_old_chroot}) {
781            (my $u_id, $chroot_tmp) = create_temp_chroot(\%run, $config,
782                $cache, $union_id, $chroot_tmp, $chroot_tar, $srpm) or next; 
783            $union_id = $u_id;
784        }
785
786        $urpmi->urpmi_command($chroot_tmp, $luser);
787        $srpm =~ /(.*)-[^-]+-[^-]+\.src\.rpm$/ or next;
788        my ($maintainer, $cc);
789        if (!$run{warn}) {
790            ($maintainer) = get_maint(\%run, $srpm);
791            $cc = $maint{$srpm};#, maintainers\@mandriva.com";
792            chomp $maintainer;
793            if (!$maintainer || $maintainer eq 'NOT_FOUND') {
794                $maintainer = $cc;
795                #$cc = 'maintainers@mandriva.com'
796            }
797        }
798        #($maintainer, $cc) = ($config->{admin},'');
799
800        plog('DEBUG', "creating user $luser in chroot");
801        add_local_user($chroot_tmp, \%run, $config, $luser, $run{uid}) or next;
802
803        my $old_srpm = $srpm;
804        my ($ret, $srpm, $spec) = $urpmi->recreate_srpm(\%run, $config,
805                        $chroot_tmp, $dir, $srpm, $luser, $retry);
806        if ($ret == -1) {
807            $retry = 1;
808            goto retry;
809        } elsif (!$ret) {
810            # CM: experimental: fail if we can't regenerate the srpm
811            #     This should eliminate bouncers that block the input queue
812            #
813            $srpm = $old_srpm;
814            $cache->{failure}{$srpm} = 1;
815            $run{status}{$srpm} = 'recreate_srpm_failure';
816            dump_cache_par(\%run);
817            dump_status($local_spool, \%run);
818            next;
819        } 
820
821        (my $log_dirname = $srpm) =~ s/.*:(.*)\.src.rpm/$1/;
822        my $log_dir = "$local_spool/log/$log_dirname/";
823
824        # only create the log dir for the new srpm
825        mkdir $log_dir;
826        -d $log_dir or die "FATAL: could not create $log_dir (check permissions and group ownerships)";
827       
828        plog('INFO', "Install build dependencies");
829        my $path_srpm = "$chroot_tmp/home/$luser/rpm/SRPMS/";
830       
831        # on x86_64 the rpm database is getting corrupted and sometimes
832        # rpm do not found anymore installed packages, retrying several
833        # time to be sure something is really broken
834
835        my $ok = $urpmi->install_packages($srpm, $chroot_tmp, $local_spool, \%pack_provide, 'install_deps', "[REBUILD] install of build dependencies of $srpm failed on $run{my_arch}", { maintainer => $maintainer }, "$path_srpm/$srpm");
836        if (!$ok) {
837            $run{status}{$srpm} ||= 'install_deps_failure';
838            next;
839        }
840
841        # try to workarround the rpm -qa db4 error(2) from dbcursor->c_get:
842        # No such file or directory
843        # system("sudo chroot $chroot_tmp rm -rf /var/lib/rpm/__db* &> /dev/null");
844        system("$sudo chroot $chroot_tmp rpm --rebuilddb &> /dev/null");
845
846        perform_command("$sudo chroot $chroot_tmp rpm -qa", 
847            \%run, $config, $cache, 
848            logname => "rpm_qa", 
849            hash => "rpm_qa_$srpm", 
850            timeout => 60, 
851            debug_mail => $run{debug},
852            log => $log_dir); # or next; As this failed quite often, do not stop
853        plog('NOTIFY', "Building $srpm");
854        my $command = "rpm --rebuild $run{with_flags} /home/$luser/rpm/SRPMS/$srpm";
855        if ($run{stop}) {
856            $urpmi->install_packages('chroot', $chroot_tmp, $local_spool, \%pack_provide, 'configure', "[ADMIN] installation of urpmi and sudo failed in the chroot $run{my_arch}", { check => 1, maintainer => $config->{admin} }, 'urpmi', 'sudo');
857            add_sudoers(\%run, $chroot_tmp, $luser);
858            $command = "rpm -b$run{stop} /home/$luser/rpm/SPECS/$spec";
859        }
860
861        my ($srpm_name) = $srpm =~ /(?:.*:)?(.*)-[^-]+-[^-]+\.src\.rpm$/;
862
863        if (!perform_command(qq(TMP=/home/$luser/tmp/ $sudo chroot $chroot_tmp /bin/su - $luser -c "$command"), 
864                \%run, $config, $cache, 
865                mail => $maintainer, 
866                error => "[REBUILD] $srpm from $run{distro_tag} does not build correctly on $run{my_arch}", 
867                logname => "build", 
868                hash => "build_$srpm", 
869                timeout => $config->{build_timeout}{$srpm_name} ? $config->{build_timeout}{$srpm_name} : $config->{build_timeout}{default},
870                srpm => $srpm,
871                debug_mail => $run{debug},
872                cc => $cc, 
873                log => $log_dir, 
874                error_regexp => 'rror.*ailed|Bad exit status|RPM build error',
875                callback => sub { 
876                    my ($opt, $output) = @_;
877                    if ($run{stop}) {
878                        plog("dumping to a chrooted shell into $chroot_tmp (pid $$)");
879                        # exec does not work because it seems stdin and out are shared between children
880                        system($sudo, 'chroot', $chroot_tmp, '/bin/su', '-', $luser, '-c', "$config->{prompt} bash");
881                        exit();
882                    }
883                    plog('DEBUG', "calling callback for $opt->{hash}");
884                    if ($run{unionfs_tmp} && $output =~ /no space left on device/i) {
885                        plog('ERROR', "ERROR: running out of space to compile $srpm in unionfs mode, will recompile it in normal mode");
886                        $cache->{no_unionfs}{$srpm} = 1;
887                        return 1;
888                    } elsif ($run{unionfs_tmp} && $output =~ m,$home,) {
889                        plog('ERROR', "ERROR: seems like building $srpm needs to access /proc/self/exe, which is broken with unionfs, will try to recompile it in non unionfs mode");
890                        $cache->{no_unionfs}{$srpm} = 1;
891                        return 1;
892                    } elsif ($output =~ /bin\/ld: cannot find -l(\S*)|configure.*error.* (?:-l([^\s]+)|([^\s]+) includes)/) {
893                        my $missing = $1;
894                        my @rpm = find_provides(\%run, \%pack_provide, $missing);
895                        plog(5, "likely @rpm ($missing-devel) needed to rebuilt $srpm is not in build_requires");
896                        if ($maintainer ne 'NOT_FOUND') {
897                            $opt->{mail} = $maintainer;
898                            #$opt->{mail} .= ", other_maint";
899                        }
900                        if (!$opt->{mail}) { 
901                            $opt->{mail} = $config->{admin};
902                        }
903                        if (@rpm > 1) {
904                            $opt->{error} = "[MISSING_BUILD_REQUIRES_TAG] one of @rpm ($missing-devel), needed to build $srpm, is not in buildrequires";
905                        } elsif (@rpm == 1) {
906                            $opt->{error} = "[MISSING_BUILD_REQUIRES_TAG] @rpm ($missing-devel), needed to build $srpm, is not in buildrequires";
907                        } else {
908                            $opt->{error} = "[MISSING_BUILD_REQUIRES_TAG] $missing-devel, needed to build $srpm, is not in buildrequires";
909                        }
910                        $cache->{buildrequires}{$srpm}{$missing} = \@rpm;
911                        return;
912                    }
913                    1;
914                }, 
915                freq => 1)) {
916
917            # FIXME
918            # The simple algo used here is :
919            #  try to compile it with unionfs, if it runs out of space,
920            #  compile it without the next time
921            #
922            #  This could be improved in keeping this srpm name for future
923            #  version, but if we compile it on a new machine with more ram,
924            #  or if next version compiles just fine with unionfs, we will
925            #  loose the unionfs advantage.
926            #
927            #  Maybe the right thing to do would be to first try to increase
928            #  the tmpfs size (more than 50 % of the physical RAM), but this
929            #  will lead to more swap usage, and slower compilation (and lost
930            #  of the unionfs plus). Or to keep the faulty package a unionfs
931            #  exception for some time, to save some more extra builds.
932
933            if (!glob "$chroot_tmp/home/$luser/rpm/RPMS/*/*.rpm") {
934                if ($run{unionfs_tmp} && $cache->{no_unionfs}{$srpm}) {
935                    goto retry;
936                }
937                $cache->{failure}{$srpm} = 1;
938                $run{status}{$srpm} = 'build_failure';
939                # 20060615
940                dump_cache_par(\%run);
941                dump_status($local_spool, \%run);
942                next;
943            }
944        }
945
946        # do some cleaning if the compilation is successful
947        delete $cache->{needed}{$srpm} if defined $cache->{needed}{$srpm};
948        delete $cache->{buildrequires}{$srpm} if defined $cache->{buildrequires}{$srpm};
949        # FIXME It seems the glob is not correctly expanded any more, so listing the directory content to do so
950        opendir my $binfh, "$chroot_tmp/home/$luser/rpm/RPMS/";
951        my @packages;
952        foreach my $bindir (readdir $binfh) {
953            -d "$chroot_tmp/home/$luser/rpm/RPMS/$bindir" or next;
954            opendir my $rpmfh, "$chroot_tmp/home/$luser/rpm/RPMS/$bindir";
955            push @packages, map { "$chroot_tmp/home/$luser/rpm/RPMS/$bindir/$_" } grep { !/src\.rpm$/ && /\.rpm$/ } readdir $rpmfh;
956        }
957
958        # 20060810 warly We should fail here, but rpm is currently
959        # segfaulting when trying to install packages
960
961        if ($config->{check_binary_file}) {
962            $urpmi->install_packages($srpm, $chroot_tmp, $local_spool, \%pack_provide, 'binary_test', "[REBUILD] binaries packages generated from $srpm do not install correctly", { maintainer => $maintainer } ,@packages) or next;
963        } else  {
964            my $successfile = "$local_spool/log/$srpm/binary_test_$srpm-1.log";
965            open my $f, ">$successfile";
966            print $f "$srpm build ok";
967        }
968       
969        $run{status}{$srpm} = 'ok';
970        delete $cache->{failure}{$srpm} if defined $cache->{failure}{$srpm};
971        if ($run{debug}) {
972            plog("debug mode, skip other packages");
973            exit();
974        } elsif ($run{group}) {
975            # we should not move the package until they are all compiled
976            plog("group mode, keep packages for local media ($srpm is done $done)");
977            $run{done}{$srpm} = $done;
978            $urpmi->add_to_local_media($chroot_tmp, $srpm, $luser);
979        } else { 
980            plog('OK', "build successful, copying packages to $local_spool.");
981
982            system("cp $chroot_tmp/home/$luser/rpm/RPMS/*/*.rpm $local_spool &>/dev/null") and plog('ERR', "ERROR: could not copy rpm files from $chroot_tmp/home/$luser/rpm/RPMS/ to $local_spool ($!)");
983
984            if ($run{copy_srpm}) {
985                # replace the old srpm
986                unlink "$local_spool/$old_srpm";
987
988                system("cp $chroot_tmp/home/$luser/rpm/SRPMS/$srpm $local_spool &>/dev/null") and plog('ERR', "ERROR: could not copy $srpm from $chroot_tmp/home/$luser/rpm/SRPMS/ to $local_spool ($!)");
989            }
990            process_queue($config, \%run, \@wrong_rpm, 1);
991        }
992        # dymp_cache each time so that concurrent process can get updated
993        dump_cache_par(\%run) if $run{concurrent_run};
994    }
995    if ($run{group}) {
996        for (my $i; $i < @{$run{todo}}; $i++) {
997            my (undef, $srpm) = @{$run{todo}[$i]};
998            if (!$run{done}{$srpm}) {
999                $rebuild = $urpmi->order_packages($union_id, \%provides, $luser);
1000                last
1001            }
1002        }
1003        if ($prev_done == $done) {
1004            $rebuild = 0;
1005            if ($done == @{$run{todo}}) {
1006                plog('OK', "all packages succesfully compiled, copying packages to $local_spool.");
1007                system("cp $chroot_tmp/home/$luser/rpm/RPMS/*/*.rpm $local_spool &>/dev/null") and plog('ERR', "ERROR: could not copy rpm files from $chroot_tmp/home/$luser/rpm/RPMS/ to $local_spool ($!)");
1008                if ($run{copy_srpm}) {
1009                    system("cp $chroot_tmp/home/$luser/rpm/SRPMS/*.src.rpm $local_spool &>/dev/null") and plog('ERR', "ERROR: could not copy SRPMS from $chroot_tmp/home/$luser/rpm/SRPMS/ to $local_spool ($!)");
1010                }
1011            } else {
1012                plog('FAIL', "some packages could not be compiled.");
1013            }
1014        }
1015    }
1016} while $rebuild;
1017
1018my ($unionfs_dir) = $run{unionfs_dir} =~ m!(.*)/[^/]+/?!;
1019if (!$run{debug} && !$run{use_old_chroot}) {
1020    if ($run{unionfs}) {
1021        clean_unionfs("$unionfs_dir/$run{user}", \%run, $run{run}, $union_id);
1022    } else {
1023        clean_chroot($chroot_tmp, $chroot_tar, \%run, $config, 1);
1024    }
1025}
1026plog("reprocess generated packages queue");
1027process_queue($config, \%run, \@wrong_rpm);
1028
1029dump_cache_par(\%run);
1030
1031plog('FAIL', "ERROR: RPM with a wrong SRPM name") if @wrong_rpm;
1032if (@wrong_rpm && open my $file, ">$local_spool/log/wrong_srpm_names.log") {
1033    foreach (@wrong_rpm) {
1034        print $file "$_->[1] -> $_->[0] (", $cache->{rpm_srpm}{$_->[1]}, ")\n";
1035    }
1036}
1037
1038dump_status($local_spool, \%run);
1039
1040send_status_mail(\%run, $config, $cache) if $run{status_mail};
1041
1042if ($config->{rsync_to} && !$run{no_rsync}) { 
1043        # remove some old and very big log files not to saturate the server
1044        system(qq(find $local_spool/log/ -name "*.log" \\( -size +$config->{log_size_limit} -or -mtime +$config->{log_size_date} \\) -exec rm -f {} \\;));
1045        system('rsync', '--delete', '-alHPe', 'ssh -xc arcfour', "$local_spool/log/", "$config->{rsync_to}/$run{distro_tag}/$run{my_arch}/$run{media}/log/");
1046}
1047
1048# one last try to clean
1049plog('DEBUG', "clean remaining unionfs");
1050if ($run{unionfs} && !$run{use_old_chroot}) {
1051    remove_chroot(\%run, $unionfs_dir, \&clean_all_unionfs);
1052}
1053unlink "$run{pidfile_home}/$run{pidfile}" if $run{pidfile};
1054
1055exit;
1056
1057
1058#
1059#
1060#
1061
1062sub check_needed {
1063    my ($srpm, $cache, $provides) = @_;
1064    if (!defined $cache->{needed}{$srpm} && !ref $cache->{needed}{$srpm}) { return 1 } 
1065    my $ok = 1;
1066    # migrate old cache format
1067    my $ent = $cache->{needed}{$srpm};
1068    if (ref $ent eq 'ARRAY') {
1069        my $table = $ent;
1070        $cache->{needed}{$srpm} = {};
1071        foreach my $t (@$table) {
1072            my ($missing, $version, $maint) = @$t;
1073            $cache->{needed}{$srpm}{$missing} = {
1074                version => $version,
1075                maint => $maint
1076            };
1077        }
1078        $ent = $cache->{needed}{$srpm};
1079    }
1080    foreach my $name (keys %$ent) {
1081        my ($package, $version, $maint) = @{$ent->{$name}}{'package', 'version', 'maint'};
1082        # if packages does not exist anymore, it may have been rebuild, then try to recompute the build dependencies
1083        last if $package && !$provides->{$package};
1084        my $p_version = $provides->{$name};
1085        if ($p_version) {
1086            next if $version == $p_version;
1087            next if URPM::ranges_overlap($version, $p_version);
1088        }
1089        $ok = 0;
1090        if ($version) {
1091            $ent->{$name}{version} = $version;
1092        }
1093        my $v ||= $version;
1094        if ($package) {
1095            plog("ERROR: $srpm needs package $package which requires missing $name $v to be compiled.");
1096        } else {
1097            plog("ERROR: $srpm needs $name $v to be compiled.");
1098        } 
1099        # try to recompile it once in a while
1100        last if $cache->{warning}{"install_deps_$srpm"}{$maint}++ % 72;
1101        return 1;
1102    }
1103    delete $cache->{needed}{$srpm} if $ok;
1104    $ok;
1105}
1106
1107sub process_queue {
1108    my ($config, $run, $wrong_rpm, $quiet) = @_;
1109    return if !$run->{upload} && $quiet;
1110    my $dir = "$config->{local_upload}/iurt/$run->{distro_tag}/$run->{my_arch}/$run->{media}/";
1111    opendir my $rpmdir, $dir or return;
1112    my $urpmi = $run->{urpmi};
1113    foreach my $rpm (readdir $rpmdir) {
1114        my ($rarch, $srpm) = $urpmi->update_srpm($dir, $rpm, $wrong_rpm);
1115        $rarch or next;
1116        plog($rpm);
1117        next if !$run->{upload};
1118        # recheck if the package has not been uploaded in the meantime
1119        my $rpms_dir = "$config->{repository}/$run->{distro}/$run->{my_arch}/media/$run->{media}/";
1120        if (! -f "$rpms_dir/$rpm") {
1121            my $err = system('/usr/bin/scp', "$dir/$rpm", $config->{upload} . "/$config->{extra_subdir}/RPMS/");
1122            # try to keep the opportunity to prevent disk full
1123            if ($err) {
1124                plog("ERROR: process_queue: cannot copy $dir/$rpm to ", $config->{upload}, "/$config->{extra_subdir}/RPMS/ ($!)");
1125                next;
1126            }
1127        }
1128        if ($run->{upload_source}) {
1129
1130        }
1131        unlink "$dir/$rpm";
1132        $cache->{queue}{$srpm} = 1;
1133    }
1134    closedir $rpmdir;
1135}
1136
1137sub check_version {
1138        my ($srpm, $srpm_version) = @_;
1139        my ($srpm_name) = $srpm =~ /(.*)-[^-]+-[^-]+\.src\.rpm/;
1140        if (URPM::ranges_overlap("= $srpm", ">= $srpm_version->{$srpm_name}")) {
1141                $srpm_version->{$srpm_name} = $srpm;
1142                return 1;
1143        }
1144        0;
1145}
1146
1147sub check_pid {
1148    my ($run, $local) = @_;
1149    my $hostname = `hostname`;
1150    chomp $hostname;
1151    my $pidfile = $run->{pidfile};
1152    my $lockfile = "$run->{pidfile_home}/$pidfile.$hostname.pid.lock";
1153    plog("trying to lock $lockfile");
1154    open my $lock, ">$lockfile";
1155    my $lock_ok;
1156    # lockf seems not to work, try to workarround, but this start to create lock on the lock for the lock of the file.
1157    my $status = 1; #File::lockf::lock($lock);
1158    if (!$status) {
1159        $lock_ok = 1;
1160    } else {
1161        plog("ERROR: could not lock pid file (status $status $!)");
1162        if (! -f "$lockfile.2") {
1163            plog("using $lockfile.2 as lock file");
1164            open my $lock2, ">$lockfile.2" or die "FATAL $program_name: could not open lock file $lockfile.2";
1165            print $lock2 $$;
1166            close $lock2;
1167        }
1168    }
1169    if (!$run->{concurrent_run} && !$local) {
1170        opendir my $dir, $run->{pidfile_home};
1171        foreach my $f (readdir $dir) {
1172            my ($pid_host) = $f =~ /$pidfile\.pid\.(.*)\.pid$/ or next; 
1173            if ($pid_host ne $hostname) {
1174                my $pf = "$run->{pidfile_home}/$f";
1175                open my $test_PID, $pf;
1176                my $pid = <$test_PID>;
1177                my (@stat) = stat $pf;
1178                my $time = $stat[9];
1179                my $diff = time()-$time;
1180                my $msg = "$program_name: an other iurt is running for $run->{my_arch} on $pid_host, pid $pid, since $diff seconds";
1181                if ($diff < 36000) {
1182                    plog("$msg\n");
1183                    exit();
1184                } else {
1185                    plog("$msg, ignoring it");
1186                }
1187            }
1188        }
1189    }
1190    $run->{pidfile} .= ".$hostname.pid";
1191    $pidfile = "$run->{pidfile_home}/$run->{pidfile}";
1192    if (-f $pidfile)  {
1193        my (@stat) = stat $pidfile;
1194        open my $test_PID, $pidfile;
1195        my $pid = <$test_PID>;
1196        close $test_PID;
1197        if (!$pid) {
1198            plog("ERROR: invalid pidfile ($pid), should be <pid>");
1199            unlink $pidfile;
1200        }
1201        if ($pid && getpgrp $pid != -1) {
1202            my $time = $stat[9];
1203            my $state = `ps h -o state $pid`;
1204            chomp $state;
1205            if ($time < time()-36000 || $state eq 'Z') {
1206                plog("an other iurt pid $pid is running for a very long time or is zombie, killing it");
1207                my $i;
1208                while ($i < 5 && getpgrp $pid != -1) {
1209                    kill_for_good($pid);
1210                    $i++;
1211                    sleep 1;
1212                }
1213            } else  {
1214                plog("an other iurt is running for $run->{my_arch}, pid $pid, since ", time()-$time, " seconds");
1215                exit();
1216            }
1217        } else {
1218            plog("a previous iurt for $run->{my_arch} seems dead, cleaning.");
1219            unlink $pidfile;
1220        }
1221    }
1222    plog("setting $pidfile pid lock");
1223    open my $PID, ">$pidfile" or die "FATAL $program_name: could not open pidfile $pidfile for writing";
1224    print $PID $$;
1225    close $PID;
1226    if ($lock_ok) { 
1227        File::lockf::ulock($lock);
1228    } else {
1229        unlink "$lockfile.2";
1230    }
1231    close $lock;
1232    unlink $lockfile;
1233}
1234
1235sub check_media {
1236    my ($run, $cache, $config, $srpm_version, $wrong_rpm, $provides, $pack_provide, $maint) = @_;
1237# We could rely on only parsing the synthesis, hoping that they are correct, however this scan is very fast, so...
1238    foreach my $subdir (@{$config->{all_media}{$run->{media}}}) {
1239        my $rpms_dir = "$config->{repository}/$run->{distro}/$run->{my_arch}/media/$run->{media}/$subdir/";
1240        plog("checking current packages in $rpms_dir");
1241        opendir my $rpmdir, $rpms_dir or die "Could not open $rpms_dir: $!"; 
1242        my $urpmi = $run->{urpmi};
1243        foreach my $rpm (readdir $rpmdir) {
1244            my ($rarch, $srpm) = $urpmi->update_srpm($rpms_dir, $rpm, $wrong_rpm);
1245            $rarch or next;
1246            $cache->{queue}{$srpm} = 1;
1247            $run{status}{$srpm} = 'ok';
1248            check_version($srpm, $srpm_version);
1249        }
1250        closedir $rpmdir;
1251    }
1252
1253    foreach my $m (keys %{$config->{all_media}}) {
1254        foreach my $subdir (@{$config->{all_media}{$m}}) {
1255            my $synthesis_file = "$config->{repository}/$run->{distro}/$run->{my_arch}/media/$m/$subdir/media_info/synthesis.hdlist.cz";
1256            if (-f $synthesis_file) {
1257                plog("Parsing $synthesis_file");
1258                if (open my $syn, "zcat $synthesis_file |") { 
1259                    my @prov;
1260                    my $nb;
1261                    while (<$syn>) {
1262                        if (/^\@provides@(.*)/) {
1263                            foreach my $p (split '@', $1) {
1264                       if ($p =~ /([^[]+)(?:\[(.*)\])?/g) {
1265                           push @prov, $1;
1266                           $provides->{$1} = $2 || 1;
1267                       }
1268                            }
1269                        } elsif (/\@info\@([^@]+)@/) {
1270                            $nb++;
1271                            my $p = $1;
1272                            my ($name) = $p =~ /(.*)-[^-]+-[^-]+\./;
1273                            $provides->{$p} = 1;
1274                            foreach (@prov) {
1275                                $pack_provide->{$_} = $name;
1276                            }
1277                            @prov = ();
1278                        }
1279                    }
1280                    $nb < $config->{minimum_package_number} and die "FATAL $program_name: synthesis files seems corrupted, only $nb packages found.";
1281                } else {
1282                    die "FATAL $program_name: Could not open $synthesis_file\n";
1283                }
1284            }
1285        }
1286    }
1287    #"
1288    my $nb;
1289    foreach my $subdir (@{$config->{all_media}{$run->{media}}}) {
1290        $nb += search_packages(0, $cache, $provides, $run, $maint, $srpm_version, "$config->{repository}/$run->{distro}/SRPMS/$run->{media}/$subdir/");
1291    }
1292    $nb;
1293}
1294
1295sub search_packages {
1296    my ($clean, $cache, $provides, $run, $_maint, $srpm_version, @dir) = @_;
1297    my ($to_compile, %rep);
1298    plog("iurt search_package: @dir");
1299    foreach my $dir (@dir) {
1300        plog("checking SRPMS dir $dir");
1301        opendir my $rpmdir, $dir or next;
1302        foreach my $srpm (readdir $rpmdir) {
1303            # this is for the output of the new svn system
1304            if ($srpm =~ /^\@\d+:(.*)/) {
1305                link "$dir/$srpm", "$dir/$1";
1306                # unlink "$dir/$srpm";
1307                $srpm = $1;
1308            }
1309            $srpm =~ /(.*)-[^-]+-[^-]+\.src\.rpm$/ or next;
1310            $run->{status}{$srpm} ||= 0;
1311            if ($config->{unwanted_packages} && $srpm =~ /$config->{unwanted_packages}/) { next }
1312            my $ok = 1;
1313            if (check_version($srpm, $srpm_version)) { 
1314                if (!$run->{ignore_failure} && defined $cache->{failure}{$srpm}) {
1315                    $run->{status}{$srpm} = 'build_failure';
1316                    next;
1317                }
1318                my $check_needed = check_needed($srpm, $cache, $provides);
1319                $run->{status}{$srpm} = 'missing_buildrequires' if !$check_needed;
1320                -f "$dir/$srpm" or next;
1321                if (!$cache->{queue}{$srpm} && $check_needed) {
1322                    if (!check_arch("$dir/$srpm", $run{my_arch})) {
1323                        $run->{status}{$srpm} = 'not_on_this_arch';
1324                        next;
1325                    }
1326                    my $hdr = RPM4::Header->new("$dir/$srpm");
1327                    my $changelog = $hdr->queryformat("%{CHANGELOGNAME}");
1328                    my ($mail) = $changelog =~ /<(.*@.*)>/;
1329                    $maint{$srpm} = $mail;
1330                    print "$program_name: will try to compile $srpm\n";
1331                    $to_compile++;
1332                    push @{$run->{todo}}, [ $dir , $srpm, 1 ];
1333                }
1334                foreach my $arch (@{$config->{supported_arch}}) { #FIXME: (tv) this loop looks suspiciously broken
1335                    $ok &&= $cache->{queue}{$srpm};
1336                }
1337            }
1338            if ($clean && ($rep{$srpm} || $ok)) {
1339                print "$program_name: cleaning $dir/$srpm\n";
1340                unlink "$dir/build/$srpm";
1341                unlink "$dir/$srpm";
1342            }
1343            $rep{$srpm} = 1;
1344        }
1345        closedir $rpmdir;
1346    }
1347    $to_compile;
1348}   
1349
1350sub add_sudoers {
1351    my ($_run, $chroot, $user) = @_;
1352    my $file = "$chroot/etc/sudoers";
1353    my $f;
1354    if (!open $f, qq(| $sudo sh -c "cat > $file")) {
1355        plog("ERROR: could not open $file ($!)");
1356        return 0;
1357    }
1358    print $f qq(Cmnd_Alias RPM=/bin/rpm,/usr/sbin/urpmi,/usr/sbin/urpme,/usr/sbin/urpmi.addmedia,/usr/sbin/urpmi.update,/usr/sbin/urpmi.removemedia
1359root    ALL=(ALL) ALL
1360$user   ALL=(ALL) NOPASSWD:RPM
1361);
1362    close $f;
1363    plog("adding sudo for /bin/rpm, /usr/sbin/urpmi and /usr/sbin/urpme");
1364    -f $file or return 0;
1365    1;
1366}
1367
1368sub dump_status {
1369    my ($local_spool, $run) = @_;
1370    my $media = $run->{media} ? "$run->{media}." : "";
1371    if (open my $file, ">$local_spool/log/status.${media}log") {
1372        foreach my $srpm (sort keys %{$run->{status}}) {
1373            print $file "$srpm: ";
1374            if ($run{status}{$srpm}) {
1375                print $file $run->{status}{$srpm};
1376            } else {
1377                print $file "unknown";
1378            }
1379            print $file "\n";
1380        }
1381    }
1382}
1383
1384#
1385# CM: FIXME: should notify in case of recreate_srpm_failure
1386#
1387
1388sub send_status_mail {
1389    my ($run, $config, $cache) = @_;
1390    my %output;
1391
1392    print "iurt compilation status\n";
1393
1394    foreach my $rpm (keys %{$run->{status}}) {
1395        next if $run->{status}{$rpm} =~ /ok|not_on_this_arch/;
1396
1397        if ($run->{status}{$rpm} eq 'missing_buildrequires') {
1398            foreach my $missing (keys %{$cache->{needed}{$rpm}}) {
1399                my $h = $cache->{needed}{$rpm}{$missing};
1400                my $maint = $h->{maint} || 'Other';
1401                my $package = $h->{package};
1402                if ($package) {
1403                    push @{$output{missing}{$maint}{$package}{$missing}{$h->{version}}}, $rpm;
1404                } else {
1405                    $output{missing}{$maint}{$rpm}{$missing}{$h->{version}} = 1;
1406                }
1407            }
1408        } elsif ($run->{status}{$rpm} eq 'build_failure') {
1409            my ($maint) = get_maint($run, $rpm);
1410            if ($cache->{buildrequires}{$rpm}) {
1411                push @{$output{buildrequires}{$maint}}, $rpm;
1412            } else {
1413                push @{$output{build}{$maint}}, $rpm;
1414            }
1415        } elsif (!$run->{status}{$rpm}) {
1416            # need to find something more usefull to do at that point
1417            next;
1418        }
1419    }
1420
1421    my $text = "*** Missing buildrequires tag in specfile ***\n";
1422    foreach my $maint (keys %{$output{buildrequires}}) {
1423        $text .= "\n$maint\n";
1424        foreach my $pack (keys %{$output{missing}{$maint}}) {
1425            foreach my $missing (keys %{$cache->{buildrequires}{$pack}}) {
1426                my $rpms = $cache->{buildrequires}{$pack}{$missing};
1427                if (@$rpms) {
1428                    $text .= "  $pack should have a buildrequires on @$rpms (for $missing-devel)\n";
1429                } else {
1430                    $text .= "  $pack should have a buildrequires for $missing-devel\n";
1431                }
1432            }
1433        }
1434    }
1435
1436    $text = "*** Missing dependencies ***\n";
1437    foreach my $maint (keys %{$output{missing}}) {
1438        $text .= "\n$maint\n";
1439        foreach my $pack (keys %{$output{missing}{$maint}}) {
1440            foreach my $missing (%{$output{missing}{$maint}{$pack}}) {
1441                my $h = $output{missing}{$maint}{$pack}{$missing};
1442                foreach my $version (keys %$h) {
1443                    if (ref $h->{$version}) {
1444                        $text .= "  $pack should be recompile because\n  $missing " . ($version ? "$version " : '') . "is not provided anymore\n";
1445                        $text .= "    to compile " . join("\n               ", @{$h->{$version}}) . "\n";
1446                    } else {
1447                        $text .= "  $pack needs $missing " . ($version ? "$version " : '') . "\n";
1448                    }
1449                }
1450            }
1451        }
1452    }
1453    $text .=  "\n*** Build failure ***\n";
1454    foreach my $maint (keys %{$output{build}}) {
1455        $text .= "\n$maint\n";
1456        foreach my $rpm (@{$output{build}{$maint}}) {
1457            $text .= "  $rpm (see $config->{log_url}/$run{distro_tag}/$run{my_arch}/$run->{media}/log/$rpm/)\n";
1458        }
1459    }
1460    print "$text\n";
1461    sendmail($run->{status_mail}, '' , "Iurt report for $run->{my_arch}/$run->{media}", $text, "Iurt the rebuild bot <$config->{admin}>", 0);
1462}
1463
1464sub find_provides {
1465    my ($_run, $pack_provide, $p) = @_;
1466    my @rpm;
1467    foreach my $provides (keys %{pack_provide}) {
1468        if ($provides =~ /$p/ && $provides =~ /devel/) {
1469            push @rpm, $pack_provide->{$provides};
1470        }
1471    }
1472    @rpm;
1473}
1474
1475sub check_sudo_access() {
1476    open my $right, "$sudo -l |";
1477    return 1 if ! $<;
1478    local $_;
1479    while (<$right>) {
1480        /\(ALL\)\s+NOPASSWD:\s+ALL/ and return 1;
1481    }
1482    0;
1483}
1484
1485__END__
1486
1487Discussion
1488
148920061222 Warly
1490  Group building
1491  For the group building, we need to order the source packages, the problem is that we do not
1492  really know what will be the provides of the resulting packages before building the.
1493  We could guess them by looking to older version, but that means that we need to have an access to
1494  the media deps files (synthesis should be enough).
1495  We can also perform a first pass of build to check which package build and then what are their
1496  provides. For the second pass, we will them be able to use the previously build packages to
1497  solve buildrequires.
Note: See TracBrowser for help on using the repository browser.