Adapt to mock-1.4.1-1.fc25
[Fedora-Rebuild.git] / lib / Fedora / Rebuild.pm
blob27f0249def5011bdf980e18a7535dc8229b0345e
1 package Fedora::Rebuild;
2 use strict;
3 use warnings;
4 use version 0.77; our $VERSION = version->declare("v0.12.1");
6 use Moose;
7 use Moose::Util::TypeConstraints;
8 use File::Path::Tiny;
9 use Carp;
10 use IO::Handle;
11 use Fedora::Rebuild::Types qw( Mode );
12 use Fedora::Rebuild::Execute;
13 use Fedora::Rebuild::Mock;
14 use Fedora::Rebuild::Package;
15 use Fedora::Rebuild::Package::Config;
16 use Fedora::Rebuild::Repository;
17 use Fedora::Rebuild::Scheduler;
18 use Fedora::Rebuild::Set::Package;
19 use Fedora::Rebuild::Solver;
20 use namespace::clean;
22 has 'all' => (is => 'ro', isa => 'Str', required => 1);
23 has 'done' => (is => 'ro', isa => 'Str', required => 1);
24 has 'failed' => (is => 'ro', isa => 'Str', required => 1);
25 # A file to log build order. Default is 'log'.
26 has 'log' => (is => 'ro', isa => 'Str', required => 0, default => 'log');
27 has 'workdir' => (is => 'ro', isa => 'Str', required => 1);
28 # Directory where repository with built packages live. This is needed for
29 # `mock' build mode.
30 has 'repodir' => (is => 'ro', isa => 'Str', required => 1);
31 # Reference to array of repository URLs. Usually points to Fedora mirror or
32 # Koji repository (baseurl in YUM terminology). This is needed for `mock'
33 # build mode.
34 has 'repourls' => (is => 'rw', isa => 'ArrayRef[Str]', required => 1);
35 # YUM packages to install at mock chroot initialization. Separate them by
36 # a space.
37 # '@buildsys-build' is needed for mirrored Fedora repositories
38 # '@build' is needed for direct koji repositories.
39 # Default value is '@buildsys-build'.
40 has mock_install_packages => ( is => 'ro', isa => 'Str', required => 0,
41 default => '@buildsys-build');
42 # RPM architecture name, e.g. x86_64.
43 # TODO: Get current architecture by rpmGetArchInfo() from librpm
44 has 'architecture' => (is => 'ro', isa => 'Str', required => 1);
45 # pyrpkg front-end for handling dist-git repositories. Default is `fedpkg'.
46 has 'pyrpkg' => (is => 'ro', isa => 'Str', required => 0, default => 'fedpkg');
47 # Koji client command. Default is `koji'.
48 has 'koji' => (is => 'ro', isa => 'Str', required => 0, default => 'koji');
49 # Clone git repositories anonymously. This will not work with `koji' mode.
50 # Default is non-anonymous clone which requires preconfigured SSH public key
51 # authentication.
52 has 'anonymous' => (is => 'ro', isa => 'Bool', required => 0, default => 0);
53 # Git branch name
54 # "f14", "f15" etc. Use "rawhide" for latest one.
55 has 'dist' => (is => 'ro', isa => 'Str', required => 1);
56 # Fedpkg dist name identifier. This is required only if git branch name is not
57 # an offical one (e.g. a private branch).
58 # "f14", "f15" etc. Use "f18" for the latest one.
59 has 'pkgdist' => (is => 'ro', isa => 'Maybe[Str]', required => 0,
60 default => undef);
61 # Build target name
62 # "dist-f14", "dist-f15" etc. Use "dist-rawhide" for latest one.
63 has 'target' => (is => 'ro', isa => 'Str', required => 1);
64 has 'message' => (is => 'ro', isa => 'Str', required => 1);
65 # Reference to function with three arguments (BuildRequire name, relation
66 # flag, version) returning false the BuildRequire should be considered, true
67 # otherwise. If attribute is undef or missing, no filtering will be performed
68 # (i.e. the same effect as sub {1}).
69 has 'buildrequiresfilter' => (is => 'ro', isa => 'CodeRef', required => 0);
70 # Pass "local" committing and building locally only, pass "mock" to committing
71 # localy and building in mock, pass "koji" for pushing commits and building
72 # in Koji. Default is "koji" to build in Koji.
73 has 'mode' => (is => 'ro', isa => Mode, required => 0, default => 'koji');
74 # Run rebuild in given number of threads. Default is 1.
75 has 'threads' => (is => 'ro', isa => subtype('Int' => where {$_>0} =>
76 message {"Attribute threads must be positive integer (was $_)"}),
77 required => 0);
78 # Load binary provides of already done packages or buildrequires in given
79 # number of threads. This is to lower local I/O load. Default is 1.
80 has 'loadthreads' => (is => 'ro', isa => subtype('Int' => where {$_>0} =>
81 message {"Attribute loadthreads must be positive integer (was $_)"}),
82 required => 0);
83 # Select rebuildable packages in given number of threads.
84 # Dependency solver is CPU intentsive work.
85 # Default is 1.
86 has 'selectthreads' => (is => 'ro', isa => subtype('Int' => where {$_>0} =>
87 message {"Attribute selectthreads must be positive integer (was $_)"}),
88 required => 0);
89 # Maximal count of immediately sequential failures to accept and continue
90 # rebuild process. If the limit exceeds, rebuild process will terminate. This
91 # is good to catch pathologic cases when something is obviously wrong and
92 # should be fixed before rebuilding (e.g. Koji is down, or you have not SSH
93 # key loaded into SSH agent). Use non-positive number to disable this check.
94 # Default value is 0 (i.e. not to check).
95 has 'failurelimit' => (is => 'ro', isa => 'Int', required => 0, default => 0);
96 # Build packages in dependency order. Default is true.
97 # If set to false, the packages will be built irrespective to dependecies
98 # (build- and run-time).
99 has 'ordered' => (is => 'ro', isa => 'Bool', required => 0, default => 1);
100 # Trace dependency solver verdict. Default is true.
101 # If set to false, reason why a package is (not) buildable will not be
102 # available. Contratry, it save a memory.
103 has 'verbosesolver' => (is => 'ro', isa => 'Bool', required => 0, default => 1);
105 has 'remaining_packages' => (is => 'ro', isa => 'Fedora::Rebuild::Set::Package',
106 lazy_build => 1, init_arg => undef);
107 has 'done_packages' => (is => 'ro', isa => 'Fedora::Rebuild::Set::Package',
108 lazy_build => 1, init_arg => undef);
109 has 'failed_packages' => (is => 'ro', isa => 'Fedora::Rebuild::Set::Package',
110 lazy_build => 1, init_arg => undef);
111 has 'subsequent_failures' => (is => 'rw', isa => 'Int', default => 0,
112 init_arg => undef);
113 has 'last_failed' => (is => 'rw', isa => 'Bool', default => 0,
114 init_arg => undef);
115 has 'repository' => (is => 'ro', isa => 'Fedora::Rebuild::Repository',
116 lazy_build => 1, init_arg => undef);
117 has build_config => (is => 'ro', isa => 'Fedora::Rebuild::Package::Config',
118 lazy_build => 1, init_arg => undef);
121 # Creates set of packages already rebuilt.
122 sub _build_done_packages {
123 my $self = shift;
124 my $packages = Fedora::Rebuild::Set::Package->new();
125 my $file;
127 if (not -e $self->done) {
128 print "No packages have been rebuilt yet.\n";
129 return $packages;
132 open($file, '<', $self->done) or
133 croak "Could not open `" . $self->done .
134 "' for loading list of already rebuilt packages: $!";
135 print "Loading list of already rebuilt package names...\n";
136 while (local $_ = <$file>) {
137 chomp;
138 if (m/^\s*$/) { next; }
140 my $package = Fedora::Rebuild::Package->new(name => $_,
141 workdir => $self->workdir, dist => $self->dist,
142 pkgdist => $self->pkgdist, target => $self->target,
143 message => $self->message);
144 $packages->insert($package);
145 $self->repository->insert($package);
147 if ($!) {
148 croak "Could not read list of rebuilt package names from file `" .
149 $self->done . "': $!";
151 close $file;
152 $self->repository->update;
153 print "Number of done packages: " . $packages->size() . "\n";
155 return $packages;
159 # Set counter of consequently failures to zero.
160 sub reset_failure_counter {
161 my $self = shift;
162 $self->last_failed(0);
163 $self->subsequent_failures(0);
166 # Append a line to a file. It adds a new line character.
167 sub append_to_file {
168 my ($self, $file_name, $text) = @_;
170 my $file = IO::Handle->new();
171 open($file, '>>', $file_name) or
172 croak "Could not open `" . $file_name . "' for appending: $!";
173 printf $file $text . "\n" or
174 croak "Could not write data into `" . $file_name . "': $!";
175 $file->flush && $file->sync && close($file) or
176 croak "Could not flush and close `" . $file_name . "': $!";
179 sub mark_done {
180 my ($self, $package) = @_;
182 $self->append_to_file($self->done, $package->name);
183 $self->append_to_file($self->log, $package->name);
185 $self->done_packages->insert($package);
186 $self->reset_failure_counter;
189 # Creates set of packages not yet rebuilt not already failed.
190 sub _build_remaining_packages {
191 my $self = shift;
192 my $packages = Fedora::Rebuild::Set::Package->new();
193 my $file;
195 open($file, '<', $self->all) or
196 croak "Could not open " . $self->all .
197 " for loading list of package names to rebuild: $!";
198 print "Loading list of all package names to rebuild...\n";
200 while (local $_ = <$file>) {
201 chomp;
202 if (m/^\s*$/) { next; }
203 if ($packages->contains($_)) { next; }
204 if (! $self->done_packages->contains($_) &&
205 ! $self->failed_packages->contains($_)) {
206 $packages->insert(Fedora::Rebuild::Package->new(name => $_,
207 workdir => $self->workdir, dist => $self->dist,
208 pkgdist => $self->pkgdist, target => $self->target,
209 message => $self->message));
212 if ($!) {
213 croak "Could not read list of package names to rebuild from file `" .
214 $self->all . "' :$!";
216 close $file;
218 print "Number of all packages: " .
219 ($packages->size + $self->done_packages->size
220 + $self->failed_packages->size) . "\n";
221 print "Number of not yet rebuilt packages: " . $packages->size() . "\n";
222 return $packages;
226 # Set of packages whose rebuild failed.
227 sub _build_failed_packages {
228 my $self = shift;
229 my $packages = Fedora::Rebuild::Set::Package->new();
230 my $file;
232 if (not -e $self->failed) {
233 print "No packages have failed yet.\n";
234 return $packages;
237 open($file, '<', $self->failed) or
238 croak "Could not open `" . $self->failed .
239 "' for loading list of already failed packages: $!";
240 print "Loading list of already failed package names...\n";
241 while (local $_ = <$file>) {
242 chomp;
243 if (m/^\s*$/) { next; }
245 my $package = Fedora::Rebuild::Package->new(name => $_,
246 workdir => $self->workdir, dist => $self->dist,
247 pkgdist => $self->pkgdist, target => $self->target,
248 message => $self->message);
249 $packages->insert($package);
251 if ($!) {
252 croak "Could not read list of failed package names from file `" .
253 $self->failed . "': $!";
255 close $file;
256 print "Number of failed packages: " . $packages->size() . "\n";
258 return $packages;
261 sub _build_repository {
262 my $self = shift;
263 return Fedora::Rebuild::Repository->new(path => $self->repodir);
266 # Gather build configuration into one object to ease passing it to
267 # Fedora::Rebuild::Package methods.
268 sub _build_build_config {
269 my $self = shift;
270 return Fedora::Rebuild::Package::Config->new(
271 mode => $self->mode(),
272 pyrpkg => $self->pyrpkg(),
273 koji => $self->koji(),
274 repositories => $self->repourls(),
275 architecture => $self->architecture(),
276 mock_install_packages => $self->mock_install_packages()
280 # Record package names into log of failed packages
281 sub mark_failed {
282 my ($self, $package) = @_;
284 # Log failure
285 $self->append_to_file($self->failed, $package->name);
287 # Move to list of failed
288 $self->failed_packages->insert($package);
290 # Check for very failures
291 if ($self->last_failed) {
292 $self->subsequent_failures($self->subsequent_failures + 1);
293 } else {
294 $self->last_failed(1);
295 $self->subsequent_failures(1);
297 if ($self->failurelimit > 0 &&
298 $self->subsequent_failures > $self->failurelimit) {
299 croak "More then " . $self->failurelimit .
300 " package(s) failed subsequently which is more then set " .
301 "threshold. Aborting now.\n";
305 # Load build-requires for each not-yet done package.
306 # Arguments are initialized mocks used build source packages.
307 # Return true in case of success or croaks in case of failure.
308 sub load_sourcedependencies {
309 my ($self, @mocks) = @_;
310 my @packages = $self->remaining_packages->packages;
311 my $scheduler = Fedora::Rebuild::Scheduler->new(
312 limit => $self->loadthreads,
313 name => 'Loading build-requires',
314 total => $#packages
316 my %jobs = ();
317 my $i = 0;
318 my @available_mocks = @mocks;
320 print "Loading build-time dependenices of not yet rebuilt packages...\n";
322 foreach my $package (@packages) {
323 my $mock = pop @available_mocks;
324 my $job = $scheduler->schedule($package->can('get_buildrequires'),
325 $package, $self->build_config, $self->anonymous, $mock);
326 if (! defined $job) {
327 push @available_mocks, $mock;
328 next;
330 $jobs{$job} = [ $package, $mock ];
331 my %finished = $scheduler->finish(++$i < @packages);
333 while (my ($job, $status) = each %finished) {
334 my ($package, $mock) = @{$jobs{$job}};
335 push @available_mocks, $mock;
336 if (!$$status[0]) {
337 print "Could not load build-time dependencies for not yet " .
338 "built package `" . $package->name . "'.\n";
339 print "Waiting for finishing scheduled jobs...\n";
340 $scheduler->finish(1);
341 print "All jobs loading build-time dependcies have finished.\n";
342 $self->clean_mocks(@mocks);
343 croak "Could not load build-time dependencies.\n";
348 print "Build-time dependencies of not-yet rebuilt packages loaded " .
349 "successfully.\n";
350 return 1;
354 # Load binary requires and provides of each done package.
355 # Return true in case of success or croaks in case of failure.
356 sub load_binarydependencies {
357 my $self = shift;
358 my @packages = $self->done_packages->packages;
359 my $scheduler = Fedora::Rebuild::Scheduler->new(
360 limit => $self->loadthreads,
361 name => 'Loading binary dependendies',
362 total => $#packages
364 my %jobs = ();
365 my $i = 0;
367 print "Loading binary dependenices of already rebuilt packages...\n";
369 foreach my $package (@packages) {
370 my $job = $scheduler->schedule($package->can('get_binarydependencies'),
371 $package);
372 if (! defined $job) { next; }
373 $jobs{$job} = $package;
374 my %finished = $scheduler->finish(++$i < @packages);
376 while (my ($job, $status) = each %finished) {
377 my $package = $jobs{$job};
378 if (!$$status[0]) {
379 print "Could not load binary dependencies for already built " .
380 "package `" . $package->name . "'.\n";
381 print "Waiting for finishing scheduled jobs...\n";
382 $scheduler->finish(1);
383 print "All jobs loading binary dependcies have finished.\n";
384 croak "Could not load binary dependencies\n";
389 print "Binary dependencies of already rebuilt packages loaded " .
390 "successfully.\n";
391 return 1;
395 # Decides a package is rebuildable now.
396 # Return 0 for false, 1 or true, undef for error while deciding.
397 sub is_rebuildable {
398 my ($self, $solver, $package) = @_;
399 my $is_rebuildable;
400 my $message = '';
402 if ($self->ordered) {
403 $is_rebuildable = $solver->is_buildable($package,
404 ($self->verbosesolver) ? \$message : undef);
405 if (!$self->verbosesolver) {
406 $message = 'Traceing the reason has been suppressed'
408 } else {
409 $is_rebuildable = 1;
410 $message = "Package `" . $package->name .
411 "' is buildable because unordered build mode is selected.";
414 $package->log_is_rebuildable($is_rebuildable, $message);
415 if ($self->verbosesolver) {
416 print "$message\n";
417 } else {
418 if ($is_rebuildable) {
419 print "Source package `" . $package->name .
420 "' can be rebuilt now.\n";
421 } else {
422 print "Source package `" . $package->name .
423 "' cannot be rebuilt now.\n";
426 return $is_rebuildable;
430 # Return F:R:Set:Packages than can be rebuilt now
431 sub select_rebuildable {
432 my $self = shift;
433 my @packages = $self->remaining_packages->packages;
434 my $scheduler = Fedora::Rebuild::Scheduler->new(
435 limit => $self->selectthreads,
436 name => 'Selecting buildable packages',
437 total => $#packages
439 my %jobs = ();
440 my $i = 0;
441 print "Selecting rebuildable packages...\n";
443 my $solver;
444 if ($self->ordered) {
445 print "Building providers reverse cache...\n";
446 $solver = Fedora::Rebuild::Solver->new(
447 'packages' => $self->done_packages,
448 'dependencyfilter' => $self->buildrequiresfilter);
449 print "Providers reverse cache built.\n";
451 my $rebuildables = Fedora::Rebuild::Set::Package->new;
452 foreach my $package (@packages) {
453 my $job = $scheduler->schedule($self->can('is_rebuildable'), $self,
454 $solver, $package);
455 if (! defined $job) { next; }
456 $jobs{$job} = $package;
457 my %finished = $scheduler->finish(++$i < @packages);
459 while (my ($job, $status) = each %finished) {
460 my $package = $jobs{$job};
462 if ($$status[0]) {
463 $rebuildables->insert($package);
464 } elsif (!defined $$status[0]) {
465 # Could not decide whether the $package is rebuildable. This is
466 # fatal for the package. Move it to failed packages.
467 $self->remaining_packages->delete($package);
468 $self->mark_failed($package);
473 print "Packages selected to rebuild (" . $rebuildables->size .
474 "): " . $rebuildables->string . "\n";
475 return $rebuildables;
479 # This removes trees of each F:R:Mock object passed as an argument.
480 sub clean_mocks {
481 my ($self, @mocks) = @_;
482 print "Removing mock environments...\n";
483 my $success = 1;
484 for my $mock (@mocks) {
485 if (!Fedora::Rebuild::Execute->new->do(undef, 'mock',
486 '--configdir', $mock->config_dir,
487 '--root', $mock->config_root,
488 '--scrub=all')) {
489 print "Error while removing `", $mock->config_dir,
490 "' mock environment\n";
491 $success = 0;
493 File::Path::Tiny::rm($mock->config_dir);
495 print "Removing done.\n";
496 return $success;
500 # Return array of F:R:Mock objects. The mocks will be shared and possibly
501 # initialized. Caller is responsible for cleaning them.
502 # First argument is number of mocks to prepeare.
503 # Second argument is true to initialize the mocks and keep them so.
504 # This croaks on error. It returns list of the Mock objects.
505 sub prepare_mocks {
506 my ($self, $count, $initialize) = @_;
508 my @mocks;
510 foreach my $i (1 .. $count) {
511 print "Preparing shared mock environment $i of $count...\n";
513 my $mock;
514 eval {
515 $mock = Fedora::Rebuild::Mock->new(
516 architecture => $self->architecture,
517 repositories => $self->repourls,
518 shared => 1,
519 install_packages => $self->mock_install_packages,
521 $mock->make_configuration;
524 if ($@) {
525 $self->clean_mocks(@mocks);
526 croak("Could not configure mock: $@\n");
529 if ($initialize) {
530 if (!Fedora::Rebuild::Execute->new->do(undef, 'mock',
531 '--configdir', $mock->config_dir,
532 '--root', $mock->config_root,
533 '--no-cleanup-after',
534 '--init')) {
535 $self->clean_mocks(@mocks);
536 croak("Could not initilize shared mock environment.\n");
540 push @mocks, $mock;
543 print "Shared mock environments prepared successfully.\n";
545 return @mocks;
549 # Rebuild all remaining packages
550 sub run {
551 my $self = shift;
552 if (defined $self->repository) {
553 if (! defined $self->repository->url) {
554 print "Starting repository HTTP server...\n";
555 $self->repository->update;
556 $self->repository->start;
557 push @{$self->repourls}, $self->repository->url;
559 print "Repository URL is: <" . $self->repository->url . ">\n";
563 print STDERR "Applying threaded glob() crash work-around\n";
564 local $_ = glob();
567 print "remaining_packages: " . $self->remaining_packages->string . "\n";
568 print "done_packages: " . $self->done_packages->string . "\n";
569 print "Rebuild mode: " . $self->mode .
570 " " . (($self->ordered)?"ordered":"unordered") . "\n";
572 my @src_mocks;
573 if ($self->mode eq 'koji' or $self->mode eq 'mock') {
574 # XXX: we need one more mock because a mock is selected before
575 # the scheduler blocks.
576 my $count = $self->loadthreads;
577 if ($self->mode eq 'mock' and $self->threads > $count) {
578 $count = $self->threads;
580 @src_mocks = $self->prepare_mocks(1 + $count, 1);
583 if ($self->ordered) {
584 $self->load_sourcedependencies(@src_mocks);
585 $self->load_binarydependencies;
588 my @mocks;
589 if ($self->mode eq 'mock') {
590 # xxx: we need one more mock because a mock is selected before
591 # the scheduler blocks.
592 @mocks = $self->prepare_mocks(1 + $self->threads, 0);
595 while ($self->remaining_packages->size > 0) {
596 my $rebuildable_packages = $self->select_rebuildable;
597 if ($rebuildable_packages->size == 0) {
598 printf "No more packages can be rebuilt!\n";
599 last;
602 my $scheduler = Fedora::Rebuild::Scheduler->new(
603 limit => $self->threads,
604 name => 'Building',
605 total => $rebuildable_packages->size
608 # Rebuild packages
609 my %jobs = ();
610 my @packages = $rebuildable_packages->packages;
611 my $i = 0;
612 my @available_mocks = @mocks;
613 my @available_src_mocks = @src_mocks;
615 foreach my $package (@packages) {
616 $self->remaining_packages->delete($package);
618 my $mock = pop @available_mocks;
619 my $src_mock = pop @available_src_mocks;
620 my $job = $scheduler->schedule($package->can('rebuild'), $package,
621 $self->build_config, $self->anonymous, $mock, $src_mock);
622 if (! defined $job) {
623 push @available_mocks, $mock;
624 push @available_src_mocks, $src_mock;
625 next;
627 $jobs{$job} = [ $package, $mock, $src_mock ];
628 my %finished = $scheduler->finish(++$i < @packages);
629 #print STDERR "HIT A\n";
630 #local $_ = glob();
631 #print STDERR "HIT B\n";
633 while (my ($job, $status) = each %finished) {
634 my ($package, $mock, $src_mock) = @{$jobs{$job}};
635 push @available_mocks, $mock;
636 push @available_src_mocks, $src_mock;
637 if ($$status[0]) {
638 # TODO: Push Provides into global list of available
639 # provides.
640 # XXX: Nothing here, fall through to rotation waiting,
641 # rebuilt package list tracked in $rebuildable_packages.
642 $self->reset_failure_counter;
643 } else {
644 $rebuildable_packages->delete($package);
645 $self->mark_failed($package);
651 # Wait for Koji rotation
652 # This is separeted from rebuilding process to solve trade-off between
653 # high threads number (consumes lot of memory and CPU time when
654 # starting) and the rebuild itself is much faster Koji rotation period
655 # (lot of time lost to wait for rotation).
656 %jobs = ();
657 @packages = $rebuildable_packages->packages;
658 $i = 0;
660 $scheduler = Fedora::Rebuild::Scheduler->new(
661 limit => $self->threads,
662 name => 'Waiting for build root',
663 total => $rebuildable_packages->size
666 foreach my $package (@packages) {
667 my $job = $scheduler->schedule($package->can('waitforbuildroot'),
668 $package, $self->build_config);
669 if (! defined $job) { next; }
670 $jobs{$job} = $package;
671 my %finished = $scheduler->finish(++$i < @packages);
673 while (my ($job, $status) = each %finished) {
674 my $package = $jobs{$job};
675 if ($$status[0]) {
676 $self->repository->insert($package);
677 $self->mark_done($package);
678 } else {
679 $self->mark_failed($package);
683 $self->repository->update;
684 $self->append_to_file($self->log, '# Synchronization point reached');
687 if ($self->mode eq 'koji' or $self->mode eq 'mock') {
688 $self->clean_mocks(@src_mocks);
690 if ($self->mode eq 'mock') {
691 $self->clean_mocks(@mocks);
694 if ($self->remaining_packages->size > 0) {
695 print "Following packages have unsatisfied build-time dependencies (" .
696 $self->remaining_packages->size . "): " .
697 $self->remaining_packages->string . "\n";
699 if ($self->failed_packages->size > 0) {
700 print "Following packages have failed while rebuilding (" .
701 $self->failed_packages->size . "): " .
702 $self->failed_packages->string . "\n";
703 return 0;
704 } else {
705 print "All rebuildable packages have been rebuilt.\n";
706 return 1;
711 __END__
712 =encoding utf8
714 =head1 NAME
716 Fedora::Rebuild - Rebuilds Fedora packages from scratch
718 =head1 DESCRIPTION
720 Main goal is to rebuild perl modules packages for Fedora. The rebuild is
721 driven from bottom to top, i.e. from perl interpreter to modules depending on
722 intermediate modules. This way, it's possible to upgrade perl interpreter to
723 incompatible version and to rebuild all modules against the new interpreter.
725 Procedure is following: We have a set of source package to rebuild. We
726 distill all build-time dependencies for each source package. We ignore
727 non-perl dependencies as we focus on Perl only.
729 We select all source packages that do not depend on anything and push them
730 into Koji to rebuild. Obviously, the only fulfilling package is Perl interpret
731 only.
733 Once the first subset of packages is rebuilt, we gather their binary packages
734 and distill their binary provides. These freshly available provides are put
735 into new set of available provides. At the same time we remove the rebuilt
736 subset from original set of all source packages.
738 Then we wait for build root rotation in Koji to get rebuilt binary packages
739 available in build root.
741 (Of course we could get provides of new binary packages from koji repository
742 after rotation and we can get provides through repoquery instead of
743 inspecting binary packages manually.)
745 If package rebuild fails, the package is removed from future consideration.
747 Then we return to start of this procedure to select other subset of source
748 packages whose build-time dependecies can be satisfied from set of binary
749 provides we have obtained till now.
751 This loop cycles until set of source packages is empty.
753 =head1 SYNCHRONIZATION
755 Because mass rebuild is long term issue and lot of intermediate failures can
756 emerge, one must be able to resume failed rebuild process.
758 Safe resume is assured by proper logging. Once a package has been rebuilt, its
759 name is logged into list of done packages. Restarting rebuild program with the
760 same arguments loads done packages, remove them from set of remaining
761 packages, and populates list of available provides. In additon, packages whose
762 rebuild failed are removed from set of remaining packages.
764 =head1 AUTHOR
766 Petr Písař <ppisar@redhat.com>
768 =head1 COPYING
770 Copyright (C) 2011, 2012, 2013, 2014 Petr Pisar <ppisar@redhat.com>
772 This program is free software: you can redistribute it and/or modify
773 it under the terms of the GNU General Public License as published by
774 the Free Software Foundation, either version 3 of the License, or
775 (at your option) any later version.
777 This program is distributed in the hope that it will be useful,
778 but WITHOUT ANY WARRANTY; without even the implied warranty of
779 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
780 GNU General Public License for more details.
782 You should have received a copy of the GNU General Public License
783 along with this program. If not, see <http://www.gnu.org/licenses/>.
785 =cut