/[soft]/build_system/iurt/trunk/iurt
ViewVC logotype

Contents of /build_system/iurt/trunk/iurt

Parent Directory Parent Directory | Revision Log Revision Log


Revision 8399 - (show annotations) (download)
Wed May 29 21:51:22 2013 UTC (10 years, 10 months ago) by pterjan
File size: 54399 byte(s)
Install icecream if it is enabled

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

Properties

Name Value
svn:executable *
svn:keywords Date Author Rev

  ViewVC Help
Powered by ViewVC 1.1.30