Use mock configuration in F:R:Package->build
[Fedora-Rebuild.git] / lib / Fedora / Rebuild / Package.pm
blobfeaa21b835f97672061574b002981b90dfdc298e
1 package Fedora::Rebuild::Package;
2 use strict;
3 use warnings;
4 use threads;
5 use threads::shared;
6 use Moose;
7 use File::Path;
8 use File::Spec;
9 use Fedora::Rebuild::Package::StateLock;
10 use Fedora::Rebuild::RPM;
11 use Fedora::Rebuild::Mock;
12 use Data::Dumper;
13 use namespace::clean;
15 use version 0.77; our $VERSION = version->declare("v0.8.0");
17 has 'name' => (is => 'ro', isa => 'Str', required => 1);
18 # Build-time dependencies
19 has 'requires' => (is => 'rw', isa => 'HashRef', lazy_build => 1,
20 init_arg => undef);
21 # Run-time dependencies hashed by binary package ENVR.
22 has 'runrequires' => (is => 'rw', isa => 'HashRef', lazy_build => 1,
23 init_arg => undef);
24 # Run-time provides hashes by binary package ENVR.
25 has 'provides' => (is => 'rw', isa => 'HashRef', lazy_build => 1,
26 init_arg => undef);
27 has 'workdir' => (is => 'ro', isa => 'Str', required => 1);
28 # Git branch name
29 # "f14", "f15" etc. Use "rawhide" for latest one.
30 has 'dist' => (is => 'ro', isa => 'Str', required => 1);
31 # Build target name
32 # "dist-f14", "dist-f15" etc. Use "rawhide" for latest one.
33 has 'target' => (is => 'ro', isa => 'Str', required => 1);
34 has 'message' => (is => 'ro', isa => 'Str', required => 1);
36 has 'packagedir' => (is => 'ro', isa => 'Str', lazy_build => 1,
37 init_arg => undef);
38 has 'repodir' => (is => 'ro', isa => 'Str', lazy_build => 1,
39 init_arg => undef);
40 has 'mockdir' => (is => 'ro', isa => 'Str', lazy_build => 1,
41 init_arg => undef);
42 has 'rpmdir' => (is => 'ro', isa => 'Str', lazy_build => 1,
43 init_arg => undef);
44 has 'requiresstore' => (is => 'ro', isa => 'Str', lazy_build => 1,
45 init_arg => undef);
46 has 'runrequiresstore' => (is => 'ro', isa => 'Str', lazy_build => 1,
47 init_arg => undef);
48 has 'providesstore' => (is => 'ro', isa => 'Str', lazy_build => 1,
49 init_arg => undef);
50 has 'taskstore' => (is => 'ro', isa => 'Str', lazy_build => 1,
51 init_arg => undef);
52 has 'branch' => (is => 'ro', isa => 'Str', lazy_build => 1,
53 init_arg => undef);
55 # Make object shared between threads.
56 # XXX: Not all attributes are shared automatically.
57 around 'new' => sub {
58 my $orig = shift;
59 my $class = shift;
60 return shared_clone($class->$orig(@_));
63 # Clean package locks bound to attributes
64 sub BUILD {
65 my $self = shift;
66 for my $state ('buildrequires', 'runrequires', 'provides') {
67 Fedora::Rebuild::Package::StateLock->new(package => $self,
68 state => $state)->mark_failed;
72 # BuildRequires. Shared.
73 # Initialize to empty hash. Call get_buildrequires() to populate.
74 sub _build_requires {
75 my $self = shift;
76 my $requires :shared = shared_clone({});
77 return $requires;
80 # Run-time Requires. Shared.
81 # Initialize to empty hash. Call get_runrequires() to populate.
82 sub _build_runrequires {
83 my $self = shift;
84 my $runrequires :shared = shared_clone({});
85 return $runrequires;
88 # Run-time provides. Shared.
89 # Initialize to empty hash. Call rebuild() or get_binaryprovides() to populate.
90 sub _build_provides {
91 my $self = shift;
92 my $provides :shared = shared_clone({});
93 return $provides;
96 sub _build_packagedir {
97 my $self = shift;
98 my $directory = File::Spec->catfile($self->workdir, $self->name);
99 if (! -d $directory) {
100 File::Path::make_path($directory) or
101 die "Could not create directory $directory: $!";
103 return $directory;
106 sub _build_repodir {
107 my $self = shift;
108 return File::Spec->catfile($self->packagedir, 'repository');
111 sub _build_mockdir {
112 my $self = shift;
113 return File::Spec->catfile($self->packagedir, 'mock');
116 sub _build_rpmdir {
117 my $self = shift;
118 return File::Spec->catfile($self->packagedir, 'RPMS');
121 sub _build_requiresstore {
122 my $self = shift;
123 return File::Spec->catfile($self->packagedir, 'buildrequires.store');
126 sub _build_runrequiresstore {
127 my $self = shift;
128 return File::Spec->catfile($self->packagedir, 'runrequires.store');
131 sub _build_providesstore {
132 my $self = shift;
133 return File::Spec->catfile($self->packagedir, 'provides.store');
136 sub _build_taskstore {
137 my $self = shift;
138 return File::Spec->catfile($self->packagedir, 'task.store');
141 sub _build_branch {
142 my $self = shift;
143 if ($self->dist eq 'rawhide') {
144 return 'master';
145 } else {
146 return $self->dist;
151 # Clones package repository and switch to proper branch if not yet done.
152 # Return true on success.
153 sub clone {
154 my $self = shift;
155 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
156 state => 'clone');
157 if ($lock->is_done) { return 1; }
159 # XXX: fedpkg creates subdirectory named like package. The package name
160 # could clash with our file structure. Thus we need to clone into secure
161 # directory and move repository content to fixed name $self->repodir after
162 # that.
164 if (-d $self->repodir) { File::Path::remove_tree($self->repodir); }
165 my $tempdir = File::Spec->catfile($self->packagedir, 'tmp');
166 my $temprepodir = File::Spec->catfile($tempdir, $self->name);
167 if (-d $tempdir) { File::Path::remove_tree($tempdir); }
168 if (!File::Path::make_path($tempdir)) {
169 $lock->log("Could not create directory `" . $tempdir . "': $!\n");
170 return $lock->mark_failed;
173 if (!$lock->do($tempdir, 'fedpkg', 'clone', $self->name)) {
174 $lock->log("Could not clone `" . $self->name . "' repository.\n");
175 return $lock->mark_failed;
178 if (!rename($temprepodir, $self->repodir)) {
179 $lock->log("Could not move `" . $temprepodir . "' content to to `" .
180 $self->repodir . "'.\n");
181 return $lock->mark_failed;
183 File::Path::remove_tree($tempdir);
185 if (!$lock->do($self->repodir, 'fedpkg', 'switch-branch', $self->branch)) {
186 $lock->log("Could not switch `" . $self->name .
187 "' repository to branch `" . $self->branch . "'.\n");
188 return $lock->mark_failed;
191 return $lock->mark_done;
194 # Builds SRPM locally. Return true on success.
195 # If first argument is true, recreate SRPM forcefully.
196 # Needs `clone'.
197 sub srpm {
198 my ($self, $force) = @_;
199 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
200 state => 'srpm');
201 if ($force) { $lock->remove_lock; }
202 if ($lock->is_done) { return 1; }
204 if (!$lock->do($self->repodir, 'fedpkg', 'srpm')) {
205 $lock->log("Could not build SRPM for `" . $self->name .
206 "' package locally.\n");
207 return $lock->mark_failed;
210 return $lock->mark_done;
213 # Get current package NEVRA from sources in repository.
214 # First argument is state lock where process of getting NEVRA including
215 # potential failure is logged.
216 # XXX: The state is not marked as failed in case of error,
217 # Return NEVRA string or undef in case of error.
218 sub get_nevra_from_git {
219 my ($self, $lock) = @_;
221 my $nevra;
222 if (!$lock->dooutput($self->repodir, \$nevra, 'fedpkg', 'verrel') ||
223 $nevra eq '') {
224 $lock->log("Could not get NEVRA from `" . $self->name .
225 "' git repository package.\n");
226 return undef;
228 chomp $nevra;
229 # Consider last line only becuase of bug in fedpkg
230 # <https://bugzilla.redhat.com/show_bug.cgi?id=721389>.
231 my @lines = (split qr{$/}, $nevra);
232 $nevra = pop @lines;
233 return $nevra;
237 # Get current package SRPM name.
238 # If the SRPM file does not exist, it will be re-created.
239 # First argument is state lock where process of building SRPM including
240 # potential failure is logged.
241 # XXX: The state is not marked as failed in case of error,
242 # Return SRPM file name string or undef in case of error.
243 sub get_srpm_name {
244 my ($self, $lock) = @_;
246 my $nevra = $self->get_nevra_from_git($lock);
247 if (! defined $nevra) {
248 return undef;
251 my $srpmname = File::Spec->catfile($self->repodir, $nevra . '.src.rpm');
252 if (! -f $srpmname ) {
253 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
254 "trying to create SRPM again...\n");
255 if (!$self->srpm(1) || ! -f $srpmname) {
256 $lock->log("`Could not recreate SRPM package '" . $srpmname .
257 "'.\n");
258 return undef;
261 return $srpmname;
264 # Create a directory. If it exists, it will remove it before.
265 # First argument is the directory, second argument is lock to log errors into.
266 # Return true, false in case of error.
267 # XXX: This is not a method
268 sub replace_directory {
269 my ($lock, $directory) = @_;
270 if (-d $directory) { File::Path::remove_tree($directory); }
271 if (!File::Path::make_path($directory)) {
272 $lock->log("Could not create directory `" . $directory . "': $!\n");
273 return 0;
275 return 1;
278 # Copy files into existing directory.
279 # First argument lock for logggin,
280 # second argument is destinatinon directory,
281 # The last is list of files to be copied.
282 # Return true in sucesss, false in case of error.
283 # XXX: This is not a method
284 sub copy_files_into_directory {
285 my ($lock, $directory, @files) = @_;
286 for my $file (@files) {
287 use File::Copy;
288 if (!copy($file, $directory)) {
289 $lock->log("Could not copy `". $file . "' into `". $directory .
290 "'\n");
291 return 0;
293 $lock->log("`" . $file . "' copied into `" . $directory . "'\n");
295 return 1;
298 # Destile BuildRequires from local SRPM and serialize them into file.
299 # Return true on success.
300 # Needs `srpm'.
301 # FIXME: does not work after cleaning clone or doing update.
302 sub storebuildrequires {
303 my $self = shift;
304 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
305 state => 'buildrequiresstore');
306 if ($lock->is_done) { return 1; }
308 my $nevra = $self->get_nevra_from_git($lock);
309 if (! defined $nevra) {
310 return $lock->mark_failed;
313 my $srpmname = File::Spec->catfile($self->repodir, $nevra . '.src.rpm');
314 if (! -f $srpmname ) {
315 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
316 "trying to create SRPM again...\n");
317 if (!$self->srpm(1) || ! -f $srpmname) {
318 $lock->log("`Could not recreate SRPM package '" . $srpmname .
319 "'.\n");
320 return $lock->mark_failed;
324 my $rpm = Fedora::Rebuild::RPM->new(name => $srpmname);
325 my ($requires, $envra) = $rpm->requires;
326 if (! defined $requires) {
327 $lock->log("Could not get requires of SRPM `" . $srpmname . "': ". $@
328 . "\n");
329 return $lock->mark_failed;
332 if (! $lock->nstorereference($requires, $self->requiresstore)) {
333 $lock->log("Could not store requires of SRPM `". $srpmname .
334 "' into `" . $self->requiresstore . "' file: $@\n");
335 return $lock->mark_failed;
338 $lock->log(Data::Dumper::Dumper($requires) . "\n");
339 return $lock->mark_done;
342 # Destile BuildRequires from local SRPM. Return true on success.
343 # Needs `buildrequiresstore'.
344 sub buildrequires {
345 my $self = shift;
346 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
347 state => 'buildrequires');
348 if ($lock->is_done) { return 1; }
350 my $requires = $lock->retrievereference($self->requiresstore);
351 if (! $requires) {
352 $lock->log("Could not load requires of `". $self->name .
353 "' package from `" . $self->requiresstore . "' file: $@\n");
354 return $lock->mark_failed;
356 $self->requires(shared_clone($requires));
358 $lock->log(Data::Dumper::Dumper($self->requires) . "\n");
359 return $lock->mark_done;
362 # Record verdict from dependency solver whether the package is rebuildable.
363 # This step is always redone.
364 sub log_is_rebuildable {
365 my ($self, $is_rebuildable, $message) = @_;
366 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
367 state => 'is_rebuildable');
368 $lock->remove_lock;
370 $lock->log("Solver result for possibility of rebuilding SRPM for `" .
371 $self->name . "': $message\n");
372 if (! $is_rebuildable) {
373 $lock->log("According dependency solver, this package is not " .
374 "rebuildable now.\n");
375 return $lock->mark_failed;
378 $lock->log("According dependency solver, this package is " .
379 "rebuildable now.\n");
380 return $lock->mark_done;
383 # Get binary RPM packages for the source package.
384 # If first argument is:
385 # 'koji' download them from Koji,
386 # 'local' from local build,
387 # 'mock' from mock result directory.
388 # Requires `clone'. Sould be called after `build'.
389 # Return true on success.
390 sub binaryrpm {
391 my ($self, $mode) = @_;
392 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
393 state => 'rpms');
394 if ($lock->is_done) { return 1; }
396 if (!replace_directory($lock, $self->rpmdir)) {
397 return $lock->mark_failed;
400 # TODO: Get current architecture by rpmGetArchInfo() from librpm
401 my @archs = qw(x86_64 noarch);
402 if ($mode eq 'koji') {
403 $lock->log("Getting binary RPM packages from Koji:\n");
405 my $nevra = $self->get_nevra_from_git($lock);
406 if (! defined $nevra) {
407 return $lock->mark_failed;
410 # TODO: Get all archs, remove SRPM
411 if (!$lock->do($self->rpmdir, 'koji', 'download-build',
412 (map {'--arch=' . $_ } @archs), $nevra)) {
413 $lock->log("Could get binary RPM packages for `" . $nevra . "'\n");
414 return $lock->mark_failed;
416 } elsif ($mode eq 'local') {
417 $lock->log("Getting binary RPM packages from local build:\n");
419 my @rpms = map { glob(File::Spec->catfile($self->repodir, $_,
420 '*.rpm')) } @archs;
421 if ($#rpms < 0) {
422 $lock->log("No binary RPM packages found under `" .
423 $self->repodir . "'\n");
424 return $lock->mark_failed;
427 if (!copy_files_into_directory($lock, $self->rpmdir, @rpms)) {
428 return $lock->mark_failed;
430 } elsif ($mode eq 'mock') {
431 $lock->log("Getting binary RPM packages from mock build:\n");
433 my @rpms = map { glob(File::Spec->catfile($self->mockdir,
434 ('*.' . $_ . '.rpm'))) } @archs;
435 if ($#rpms < 0) {
436 $lock->log("No binary RPM packages found under `" .
437 $self->mockdir . "'\n");
438 return $lock->mark_failed;
441 if (!copy_files_into_directory($lock, $self->rpmdir, @rpms)) {
442 return $lock->mark_failed;
444 } else {
445 $lock->log("Could get binary RPM packages for `" . $self->name .
446 "' source package because of unknown building mode `" . $mode .
447 "'\n");
448 return $lock->mark_failed;
451 return $lock->mark_done;
454 # Return list of binary RPM files relative to current working directory.
455 # XXX: Should be called after colleting the files from build process by
456 # binaryrpm().
457 sub listbinaryrpmfiles {
458 my $self = shift;
459 return (glob(File::Spec->catfile($self->rpmdir, '*.rpm')));
462 # Distill Requires from rebuilt binary packages and serialize them into file.
463 # Return true on success.
464 # Needs `rpms'.
465 sub storebinaryrequires {
466 my $self = shift;
467 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
468 state => 'runrequiresstore');
469 if ($lock->is_done) { return 1; }
471 my @rpms = glob(File::Spec->catfile($self->rpmdir, '*.rpm'));
472 if ($#rpms < 0) {
473 $lock->log("No binary RPM packages found in `" . $self->rpmdir
474 . "'\n");
475 return $lock->mark_failed;
478 my $allrequires = {};
479 for my $rpmname (@rpms) {
480 my $rpm = Fedora::Rebuild::RPM->new(name => $rpmname);
482 my ($requires, $envr) = $rpm->requires;
484 if (! defined $requires || ! defined $envr) {
485 $lock->log("Could not get run-time requires of RPM `" . $rpmname .
486 "': " . $@ ."\n");
487 return $lock->mark_failed;
489 $$allrequires{$envr} = $requires;
492 if (! $lock->nstorereference($allrequires, $self->runrequiresstore)) {
493 $lock->log("Could not store run-time requires of RPM `". $self->name .
494 "' into `" . $self->runrequiresstore . "' file: $@\n");
495 return $lock->mark_failed;
498 $lock->log(Data::Dumper::Dumper($allrequires));
499 return $lock->mark_done;
502 # Distill Provides from rebuilt binary packages and serialize them into file.
503 # Return true on success.
504 # Needs `rpms'.
505 sub storebinaryprovides {
506 my $self = shift;
507 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
508 state => 'providesstore');
509 if ($lock->is_done) { return 1; }
511 my @rpms = glob(File::Spec->catfile($self->rpmdir, '*.rpm'));
512 if ($#rpms < 0) {
513 $lock->log("No binary RPM packages found in `" . $self->rpmdir
514 . "'\n");
515 return $lock->mark_failed;
518 my $allprovides = {};
519 for my $rpmname (@rpms) {
520 my $rpm = Fedora::Rebuild::RPM->new(name => $rpmname);
522 my ($provides, $envr) = $rpm->provides;
524 if (! defined $provides || !defined $envr) {
525 $lock->log("Could not get provides of RPM `" . $rpmname . "': " .
526 $@ ."\n");
527 return $lock->mark_failed;
529 $$allprovides{$envr} = $provides;
532 if (! $lock->nstorereference($allprovides, $self->providesstore)) {
533 $lock->log("Could not store provides of RPM `". $self->name .
534 "' into `" . $self->providesstore . "' file: $@\n");
535 return $lock->mark_failed;
538 $lock->log(Data::Dumper::Dumper($allprovides));
539 return $lock->mark_done;
542 # Load run-time requires of already rebuilt binary packages from file.
543 # Return true on success.
544 # Needs `storebinaryrequires'.
545 sub loadbinaryrequires {
546 my $self = shift;
547 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
548 state => 'runrequires');
549 if ($lock->is_done) { return 1; }
551 my $runrequires = $lock->retrievereference($self->runrequiresstore);
552 if (! $runrequires) {
553 $lock->log("Could not load run-time requires of `". $self->name .
554 "' package from `" . $self->runrequiresstore . "' file: $@\n");
555 return $lock->mark_failed;
557 $self->runrequires(shared_clone($runrequires));
559 $lock->log(Data::Dumper::Dumper($self->runrequires));
560 return $lock->mark_done;
563 # Load provides of already rebuilt binary packages from file.
564 # Return true on success.
565 # Needs `storebinaryprovides'.
566 sub loadbinaryprovides {
567 my $self = shift;
568 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
569 state => 'provides');
570 if ($lock->is_done) { return 1; }
572 my $provides = $lock->retrievereference($self->providesstore);
573 if (! $provides) {
574 $lock->log("Could not load provides of `". $self->name .
575 "' package from `" . $self->providesstore . "' file: $@\n");
576 return $lock->mark_failed;
578 $self->provides(shared_clone($provides));
580 $lock->log(Data::Dumper::Dumper($self->provides));
581 return $lock->mark_done;
584 # Increase package revision if not yet done. Commit change if first argument
585 # is "koji".
586 # Return true on success.
587 # Needs `clone'.
588 sub update {
589 my ($self, $mode) = @_;
590 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
591 state => 'update');
592 if ($lock->is_done) { return 1; }
594 # Reset git repository
595 if (!$lock->do($self->repodir, 'git', 'reset', '--hard',
596 'origin/' . $self->branch)) {
597 $lock->log("Could not reset git repository in `" . $self->repodir .
598 "'.\n");
599 return $lock->mark_failed;
602 # Pull git repository
603 if (!$lock->do($self->repodir, 'git', 'pull')) {
604 $lock->log("Could not pull git repository in `" . $self->repodir .
605 "'.\n");
606 return $lock->mark_failed;
609 # Increase revision number
610 my $specfile = File::Spec->catfile($self->repodir, $self->name . '.spec');
611 if (!$lock->do(undef, 'rpmdev-bumpspec', '-c', $self->message, $specfile)) {
612 $lock->log("Could not increase revison number in `" . $specfile .
613 "' specfile.\n");
614 return $lock->mark_failed;
617 # Commit changes
618 if (!$lock->do($self->repodir, 'git', 'commit', '-a',
619 '-m', $self->message)) {
620 $lock->log("Could not commit changes into git repository `" .
621 $self->repodir . "'.\n");
622 return $lock->mark_failed;
625 if ($mode eq 'koji') {
626 # Push changes
627 if (!$lock->do($self->repodir, 'git', 'push')) {
628 $lock->log("Could not push changes from repository `" .
629 $self->repodir . "' to server.\n");
630 return $lock->mark_failed;
632 } else {
633 $lock->log("Not pushing changes because of local build mode.\n");
636 return $lock->mark_done;
640 # Submit package for building into Koji and store task ID.
641 # This is pointless in local build mode.
642 # Requires `clone'. Sould be called after `update'.
643 # Return true on success.
644 sub submitbuild {
645 my ($self) = @_;
646 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
647 state => 'submitbuild');
648 if ($lock->is_done) { return 1; }
650 # Get NEVRA of intended build
651 my $nevra = $self->get_nevra_from_git($lock);
652 if (! defined $nevra) {
653 return $lock->mark_failed;
656 # Check the build is not already in Koji (e.g. by concurrent packager
657 # or after this program restart) but do not conclude anything.
658 my $buildinfo;
659 if (!$lock->dooutput($self->repodir, \$buildinfo, 'koji', 'buildinfo',
660 $nevra)) {
661 $lock->log("Could not ask Koji for `" . $nevra . "' status " .
662 "before submitting new build.\n");
663 return $lock->mark_failed;
665 if ($buildinfo =~ /No such build/m) {
666 $lock->log("Package not yet submitted for building as expected.\n");
667 } else {
668 # Get task ID of already building package
669 if ($buildinfo =~ /Task:\s*(\d+)/m) {
670 # TODO: We could compare task target and consider as submitted if
671 # equaled to intended target.
672 my $task_id = $1;
673 $lock->log("Package `$nevra' already submitted as task " .
674 "`$task_id'. Previous build failed or somebody builds the " .
675 "package concurrently.\n");
676 } else {
677 $lock->log("Package `$nevra' already in Koji, but task ID " .
678 "could not been determined.\n");
680 $lock->log("Re-submitting the package.\n")
683 # Submit into Koji
684 my $task_id;
685 if (!$lock->dooutput($self->repodir, \$task_id, 'fedpkg', 'build',
686 '--nowait', '--target', $self->target)) {
687 $lock->log("Could not submit `" . $nevra . "' into Koji.\n");
688 return $lock->mark_failed;
690 if (not $task_id =~ /Created task:\s*(\d+)/) {
691 $lock->log("Could not parse Koji task ID for `$nevra' build\n");
692 return $lock->mark_failed;
694 $task_id = $1;
696 # Store task ID
697 if (! $lock->nstorereference(\$task_id, $self->taskstore)) {
698 $lock->log("Could not store task ID `" . $task_id . "' of `" . $nevra .
699 "' package into `" . $self->taskstore . "' file: $@\n");
700 return $lock->mark_failed;
701 } else {
702 $lock->log("Task ID `" . $task_id . "' stored into `" .
703 $self->taskstore . "' file sucessfully.\n");
706 return $lock->mark_done;
710 # If first argument is:
711 # "koji" wait for package build in Koji,
712 # "local" build locally using fedpkg.
713 # "mock" build locally using mock.
714 # Second argument is reference to array of repository URLs to use as package
715 # base when building the package.
716 # Requires `clone'. Sould be called after `update' or `submitbuild'.
717 # Return true on success.
718 sub build {
719 my ($self, $mode, $repositories) = @_;
720 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
721 state => 'build');
722 if ($lock->is_done) { return 1; }
724 my $nevra = $self->get_nevra_from_git($lock);
725 if (! defined $nevra) {
726 return $lock->mark_failed;
729 if ($mode eq 'local') {
730 if (!$lock->do($self->repodir, 'fedpkg', 'local')) {
731 $lock->log("Could not build `" . $nevra . "' locally.\n");
732 return $lock->mark_failed;
734 } elsif ($mode eq 'koji') {
735 # Retrieve task ID of submitted build
736 my $task_id = $lock->retrievereference($self->taskstore);
737 if (! $task_id) {
738 $lock->log("Could not load task ID for `". $nevra .
739 "' build from `" . $self->taskstore . "' file: $@\n");
740 return $lock->mark_failed;
742 $task_id = $$task_id;
744 # Wait for build task result
745 # TODO: How to recognize the process died for other reason
746 # than build failure?
747 if (!$lock->do($self->repodir, 'koji', 'watch-task', $task_id)) {
748 $lock->log("Could not get status of Koji task `" . $task_id .
749 "' for `$nevra' build, or the task failed.\n");
750 return $lock->mark_failed;
752 } elsif ($mode eq 'mock') {
753 my $srpm_name = $self->get_srpm_name($lock);
754 if (! defined $srpm_name) {
755 return $lock->mark_failed;
757 if (!replace_directory($lock, $self->mockdir)) {
758 return $lock->mark_failed;
760 my ($mock_config_dir, $mock_config_root);
761 eval {
762 # FIXME: Parametrize architecture
763 ($mock_config_dir, $mock_config_root) =
764 Fedora::Rebuild::Mock->new(
765 architecture => 'x86_64', repositories => $repositories
766 )->make_configuration;
768 if ($@) {
769 $lock->log("Could not configure mock: $@\n");
770 File::Path::remove_tree($mock_config_dir);
771 return $lock->mark_failed;
773 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
774 '--configdir', $mock_config_dir, '--root', $mock_config_root,
775 '--rebuild', $srpm_name)) {
776 $lock->log("Could not build `" . $nevra . "' in mock.\n");
777 File::Path::remove_tree($mock_config_dir);
778 return $lock->mark_failed;
780 File::Path::remove_tree($mock_config_dir);
781 } else {
782 $lock->log("Could not build `" . $nevra .
783 "' because of unknown building mode `" . $mode . "'.\n");
784 return $lock->mark_failed;
787 return $lock->mark_done;
791 # Waits for build root rotation. Just built package will be available for
792 # other packages at build time after returning from this fuction.
793 # If first argument is "koji", it will wait for the Koji rotation.
794 # If first argument is "mock", it will create yum repository in directory with
795 # binary packages.
796 # If first argument is "local", this function is void,
797 # Requires `update'. Sould be called after `build'.
798 # Return true on success.
799 sub dowaitforbuildroot {
800 my ($self, $mode) = @_;
801 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
802 state => 'rotate');
803 if ($lock->is_done) { return 1; }
805 my $nevra = $self->get_nevra_from_git($lock);
806 if (! defined $nevra) {
807 return $lock->mark_failed;
810 if ($mode eq 'koji') {
811 if (!$lock->do($self->repodir, 'koji', 'wait-repo',
812 '--build=' . $nevra, '--target', $self->target)) {
813 $lock->log("Koji does not contain `" . $nevra .
814 "' package in build root for `" . $self->target .
815 "' build target yet.\n");
816 return $lock->mark_failed;
818 } elsif ($mode eq 'mock') {
819 $lock->log("`" . $nevra .
820 "' built in mock, creating yum repository...\n");
821 if (!$lock->do($self->rpmdir, 'createrepo', '.')) {
822 $lock->log("Could not create yum repository for `" . $nevra .
823 "' package.\n");
824 return $lock->mark_failed;
826 $lock->log("`" . $nevra .
827 "' yum repository created successfully.\n");
828 } else {
829 $lock->log("`" . $nevra .
830 "' built locally, not waiting on Koji rotation.\n");
833 return $lock->mark_done;
836 # Set hash of build-time dependencies (requires attribute).
837 # Return undef in case of failure.
838 sub get_buildrequires {
839 my $self = shift;
840 my $ok;
841 print "Getting BuildRequires for `" . $self->name . "'...\n";
843 # Procede all steps, each must be re-doable
844 $ok = $self->clone;
845 $ok = $self->srpm if $ok;
846 $ok = $self->storebuildrequires if $ok;
847 $ok = $self->buildrequires if $ok;
849 #...;
851 if ($ok) {
852 print "BuildRequires for `" . $self->name .
853 "' package distilled successfully.\n";
854 return 1;
855 } else {
856 print "Could not get BuildRequires for `" . $self->name .
857 "' package.\n";
858 return undef;
862 # Set hash of run-time requires (Requires attribute).
863 # Return true on success, undef in case of failure.
864 # XXX: Requires `runrequiresstore'
865 sub get_binaryrequires {
866 my $self = shift;
867 my $ok;
868 print "Getting run-time requires for `" . $self->name . "'...\n";
870 $ok = $self->storebinaryrequires;
871 $ok = $self->loadbinaryrequires if $ok;
873 if ($ok) {
874 print "Run-time requires for `" . $self->name .
875 "' package distilled successfully.\n";
876 return 1;
877 } else {
878 print "Could not get run-time requires for `" . $self->name .
879 "' package.\n";
880 return undef;
884 # Set hash of run-time provides (provides attribute).
885 # Return true on success, undef in case of failure.
886 # XXX: Requires `providesstore'
887 sub get_binaryprovides {
888 my $self = shift;
889 my $ok;
890 print "Getting binary provides for `" . $self->name . "'...\n";
892 $ok = $self->storebinaryprovides;
893 $ok = $self->loadbinaryprovides if $ok;
895 if ($ok) {
896 print "Provides for `" . $self->name .
897 "' package distilled successfully.\n";
898 return 1;
899 } else {
900 print "Could not get Provides for `" . $self->name . "' package.\n";
901 return undef;
905 # Set hash of run-time requires (Requires attribute) and provides
906 # (provides attribute). Common wrapper for getbinaryrequires() and
907 # getbinaryprovides().
908 # Return true on success, undef in case of failure.
909 # XXX: Requires `runrequiresstore' and `providesstore'
910 sub get_binarydependencies {
911 my $self = shift;
912 my $ok;
913 print "Getting binary dependencies for `" . $self->name . "'...\n";
915 $ok = $self->get_binaryrequires;
916 $ok = $self->get_binaryprovides if $ok;
918 if ($ok) {
919 print "Binary dependencies for `" . $self->name .
920 "' package distilled successfully.\n";
921 return 1;
922 } else {
923 print "Could not get binary dependencies for `" . $self->name .
924 "' package.\n";
925 return undef;
929 # Rebuild a package without waiting for propagation to next build root.
930 # First argument defines build mode:
931 # "koji" publish commit and build in Koji,
932 # "local" build locally without pushing commits to server,
933 # "mock" build in mock without pushing commits to server.
934 # Second argument is reference to array of repository URLs to use as package
935 # base when building the package.
936 sub rebuild {
937 my ($self, $mode, $repositories) = @_;
938 my $ok;
939 print "Rebuilding `" . $self->name . "'...\n";
941 # Proceed all steps, each must be re-doable
942 $ok = $self->clone;
943 $ok = $self->update($mode) if $ok;
944 $ok = $self->submitbuild if ($ok and $mode eq 'koji');
945 $ok = $self->build($mode, $repositories) if $ok;
946 $ok = $self->binaryrpm($mode) if $ok;
947 $ok = $self->get_binaryprovides if $ok;
948 $ok = $self->get_binaryrequires if $ok;
950 if ($ok) {
951 print "`" . $self->name . "' rebuild finished successfully.\n";
952 } else {
953 print "`" . $self->name . "' rebuild failed.\n";
955 return $ok;
958 # Wait for the package propagated into new build root.
959 # If first argument is "koji", waits for Koji build root rotation,
960 # Otherwise waits locally (no-op).
961 # XXX: Requires `update', should be called after rebuilding package.
962 sub waitforbuildroot {
963 my ($self, $mode) = @_;
964 my $ok;
965 print "Waiting for `" . $self->name . "' to get to build root...\n";
967 $ok = $self->dowaitforbuildroot($mode);
969 if ($ok) {
970 print "`" . $self->name . "' propagated successfully.\n";
971 } else {
972 print "`" . $self->name . "' propagation failed.\n";
974 return $ok;