Create repository from built package in mock mode
[Fedora-Rebuild.git] / lib / Fedora / Rebuild / Package.pm
blobcfea5984982400f092a8f957115370f4a914d004
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 Data::Dumper;
12 use namespace::clean;
14 use version 0.77; our $VERSION = version->declare("v0.8.0");
16 has 'name' => (is => 'ro', isa => 'Str', required => 1);
17 # Build-time dependencies
18 has 'requires' => (is => 'rw', isa => 'HashRef', lazy_build => 1,
19 init_arg => undef);
20 # Run-time dependencies hashed by binary package ENVR.
21 has 'runrequires' => (is => 'rw', isa => 'HashRef', lazy_build => 1,
22 init_arg => undef);
23 # Run-time provides hashes by binary package ENVR.
24 has 'provides' => (is => 'rw', isa => 'HashRef', lazy_build => 1,
25 init_arg => undef);
26 has 'workdir' => (is => 'ro', isa => 'Str', required => 1);
27 # Git branch name
28 # "f14", "f15" etc. Use "rawhide" for latest one.
29 has 'dist' => (is => 'ro', isa => 'Str', required => 1);
30 # Build target name
31 # "dist-f14", "dist-f15" etc. Use "dist-rawhide" for latest one.
32 has 'target' => (is => 'ro', isa => 'Str', required => 1);
33 has 'message' => (is => 'ro', isa => 'Str', required => 1);
35 has 'packagedir' => (is => 'ro', isa => 'Str', lazy_build => 1,
36 init_arg => undef);
37 has 'repodir' => (is => 'ro', isa => 'Str', lazy_build => 1,
38 init_arg => undef);
39 has 'mockdir' => (is => 'ro', isa => 'Str', lazy_build => 1,
40 init_arg => undef);
41 has 'rpmdir' => (is => 'ro', isa => 'Str', lazy_build => 1,
42 init_arg => undef);
43 has 'requiresstore' => (is => 'ro', isa => 'Str', lazy_build => 1,
44 init_arg => undef);
45 has 'runrequiresstore' => (is => 'ro', isa => 'Str', lazy_build => 1,
46 init_arg => undef);
47 has 'providesstore' => (is => 'ro', isa => 'Str', lazy_build => 1,
48 init_arg => undef);
49 has 'taskstore' => (is => 'ro', isa => 'Str', lazy_build => 1,
50 init_arg => undef);
51 has 'branch' => (is => 'ro', isa => 'Str', lazy_build => 1,
52 init_arg => undef);
54 # Make object shared between threads.
55 # XXX: Not all attributes are shared automatically.
56 around 'new' => sub {
57 my $orig = shift;
58 my $class = shift;
59 return shared_clone($class->$orig(@_));
62 # Clean package locks bound to attributes
63 sub BUILD {
64 my $self = shift;
65 for my $state ('buildrequires', 'runrequires', 'provides') {
66 Fedora::Rebuild::Package::StateLock->new(package => $self,
67 state => $state)->mark_failed;
71 # BuildRequires. Shared.
72 # Initialize to empty hash. Call get_buildrequires() to populate.
73 sub _build_requires {
74 my $self = shift;
75 my $requires :shared = shared_clone({});
76 return $requires;
79 # Run-time Requires. Shared.
80 # Initialize to empty hash. Call get_runrequires() to populate.
81 sub _build_runrequires {
82 my $self = shift;
83 my $runrequires :shared = shared_clone({});
84 return $runrequires;
87 # Run-time provides. Shared.
88 # Initialize to empty hash. Call rebuild() or get_binaryprovides() to populate.
89 sub _build_provides {
90 my $self = shift;
91 my $provides :shared = shared_clone({});
92 return $provides;
95 sub _build_packagedir {
96 my $self = shift;
97 my $directory = File::Spec->catfile($self->workdir, $self->name);
98 if (! -d $directory) {
99 File::Path::make_path($directory) or
100 die "Could not create directory $directory: $!";
102 return $directory;
105 sub _build_repodir {
106 my $self = shift;
107 return File::Spec->catfile($self->packagedir, 'repository');
110 sub _build_mockdir {
111 my $self = shift;
112 return File::Spec->catfile($self->packagedir, 'mock');
115 sub _build_rpmdir {
116 my $self = shift;
117 return File::Spec->catfile($self->packagedir, 'RPMS');
120 sub _build_requiresstore {
121 my $self = shift;
122 return File::Spec->catfile($self->packagedir, 'buildrequires.store');
125 sub _build_runrequiresstore {
126 my $self = shift;
127 return File::Spec->catfile($self->packagedir, 'runrequires.store');
130 sub _build_providesstore {
131 my $self = shift;
132 return File::Spec->catfile($self->packagedir, 'provides.store');
135 sub _build_taskstore {
136 my $self = shift;
137 return File::Spec->catfile($self->packagedir, 'task.store');
140 sub _build_branch {
141 my $self = shift;
142 if ($self->dist eq 'rawhide') {
143 return 'master';
144 } else {
145 return $self->dist;
150 # Clones package repository and switch to proper branch if not yet done.
151 # Return true on success.
152 sub clone {
153 my $self = shift;
154 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
155 state => 'clone');
156 if ($lock->is_done) { return 1; }
158 # XXX: fedpkg creates subdirectory named like package. The package name
159 # could clash with our file structure. Thus we need to clone into secure
160 # directory and move repository content to fixed name $self->repodir after
161 # that.
163 if (-d $self->repodir) { File::Path::remove_tree($self->repodir); }
164 my $tempdir = File::Spec->catfile($self->packagedir, 'tmp');
165 my $temprepodir = File::Spec->catfile($tempdir, $self->name);
166 if (-d $tempdir) { File::Path::remove_tree($tempdir); }
167 if (!File::Path::make_path($tempdir)) {
168 $lock->log("Could not create directory `" . $tempdir . "': $!\n");
169 return $lock->mark_failed;
172 if (!$lock->do($tempdir, 'fedpkg', 'clone', $self->name)) {
173 $lock->log("Could not clone `" . $self->name . "' repository.\n");
174 return $lock->mark_failed;
177 if (!rename($temprepodir, $self->repodir)) {
178 $lock->log("Could not move `" . $temprepodir . "' content to to `" .
179 $self->repodir . "'.\n");
180 return $lock->mark_failed;
182 File::Path::remove_tree($tempdir);
184 if (!$lock->do($self->repodir, 'fedpkg', 'switch-branch', $self->branch)) {
185 $lock->log("Could not switch `" . $self->name .
186 "' repository to branch `" . $self->branch . "'.\n");
187 return $lock->mark_failed;
190 return $lock->mark_done;
193 # Builds SRPM locally. Return true on success.
194 # If first argument is true, recreate SRPM forcefully.
195 # Needs `clone'.
196 sub srpm {
197 my ($self, $force) = @_;
198 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
199 state => 'srpm');
200 if ($force) { $lock->remove_lock; }
201 if ($lock->is_done) { return 1; }
203 if (!$lock->do($self->repodir, 'fedpkg', 'srpm')) {
204 $lock->log("Could not build SRPM for `" . $self->name .
205 "' package locally.\n");
206 return $lock->mark_failed;
209 return $lock->mark_done;
212 # Get current package NEVRA from sources in repository.
213 # First argument is state lock where process of getting NEVRA including
214 # potential failure is logged.
215 # XXX: The state is not marked as failed in case of error,
216 # Return NEVRA string or undef in case of error.
217 sub get_nevra_from_git {
218 my ($self, $lock) = @_;
220 my $nevra;
221 if (!$lock->dooutput($self->repodir, \$nevra, 'fedpkg', 'verrel') ||
222 $nevra eq '') {
223 $lock->log("Could not get NEVRA from `" . $self->name .
224 "' git repository package.\n");
225 return undef;
227 chomp $nevra;
228 # Consider last line only becuase of bug in fedpkg
229 # <https://bugzilla.redhat.com/show_bug.cgi?id=721389>.
230 my @lines = (split qr{$/}, $nevra);
231 $nevra = pop @lines;
232 return $nevra;
236 # Get current package SRPM name.
237 # If the SRPM file does not exist, it will be re-created.
238 # First argument is state lock where process of building SRPM including
239 # potential failure is logged.
240 # XXX: The state is not marked as failed in case of error,
241 # Return SRPM file name string or undef in case of error.
242 sub get_srpm_name {
243 my ($self, $lock) = @_;
245 my $nevra = $self->get_nevra_from_git($lock);
246 if (! defined $nevra) {
247 return undef;
250 my $srpmname = File::Spec->catfile($self->repodir, $nevra . '.src.rpm');
251 if (! -f $srpmname ) {
252 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
253 "trying to create SRPM again...\n");
254 if (!$self->srpm(1) || ! -f $srpmname) {
255 $lock->log("`Could not recreate SRPM package '" . $srpmname .
256 "'.\n");
257 return undef;
260 return $srpmname;
263 # Create a directory. If it exists, it will remove it before.
264 # First argument is the directory, second argument is lock to log errors into.
265 # Return true, false in case of error.
266 # XXX: This is not a method
267 sub replace_directory {
268 my ($lock, $directory) = @_;
269 if (-d $directory) { File::Path::remove_tree($directory); }
270 if (!File::Path::make_path($directory)) {
271 $lock->log("Could not create directory `" . $directory . "': $!\n");
272 return 0;
274 return 1;
277 # Copy files into existing directory.
278 # First argument lock for logggin,
279 # second argument is destinatinon directory,
280 # The last is list of files to be copied.
281 # Return true in sucesss, false in case of error.
282 # XXX: This is not a method
283 sub copy_files_into_directory {
284 my ($lock, $directory, @files) = @_;
285 for my $file (@files) {
286 use File::Copy;
287 if (!copy($file, $directory)) {
288 $lock->log("Could not copy `". $file . "' into `". $directory .
289 "'\n");
290 return 0;
292 $lock->log("`" . $file . "' copied into `" . $directory . "'\n");
294 return 1;
297 # Destile BuildRequires from local SRPM and serialize them into file.
298 # Return true on success.
299 # Needs `srpm'.
300 # FIXME: does not work after cleaning clone or doing update.
301 sub storebuildrequires {
302 my $self = shift;
303 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
304 state => 'buildrequiresstore');
305 if ($lock->is_done) { return 1; }
307 my $nevra = $self->get_nevra_from_git($lock);
308 if (! defined $nevra) {
309 return $lock->mark_failed;
312 my $srpmname = File::Spec->catfile($self->repodir, $nevra . '.src.rpm');
313 if (! -f $srpmname ) {
314 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
315 "trying to create SRPM again...\n");
316 if (!$self->srpm(1) || ! -f $srpmname) {
317 $lock->log("`Could not recreate SRPM package '" . $srpmname .
318 "'.\n");
319 return $lock->mark_failed;
323 my $rpm = Fedora::Rebuild::RPM->new(name => $srpmname);
324 my ($requires, $envra) = $rpm->requires;
325 if (! defined $requires) {
326 $lock->log("Could not get requires of SRPM `" . $srpmname . "': ". $@
327 . "\n");
328 return $lock->mark_failed;
331 if (! $lock->nstorereference($requires, $self->requiresstore)) {
332 $lock->log("Could not store requires of SRPM `". $srpmname .
333 "' into `" . $self->requiresstore . "' file: $@\n");
334 return $lock->mark_failed;
337 $lock->log(Data::Dumper::Dumper($requires) . "\n");
338 return $lock->mark_done;
341 # Destile BuildRequires from local SRPM. Return true on success.
342 # Needs `buildrequiresstore'.
343 sub buildrequires {
344 my $self = shift;
345 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
346 state => 'buildrequires');
347 if ($lock->is_done) { return 1; }
349 my $requires = $lock->retrievereference($self->requiresstore);
350 if (! $requires) {
351 $lock->log("Could not load requires of `". $self->name .
352 "' package from `" . $self->requiresstore . "' file: $@\n");
353 return $lock->mark_failed;
355 $self->requires(shared_clone($requires));
357 $lock->log(Data::Dumper::Dumper($self->requires) . "\n");
358 return $lock->mark_done;
361 # Record verdict from dependency solver whether the package is rebuildable.
362 # This step is always redone.
363 sub log_is_rebuildable {
364 my ($self, $is_rebuildable, $message) = @_;
365 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
366 state => 'is_rebuildable');
367 $lock->remove_lock;
369 $lock->log("Solver result for possibility of rebuilding SRPM for `" .
370 $self->name . "': $message\n");
371 if (! $is_rebuildable) {
372 $lock->log("According dependency solver, this package is not " .
373 "rebuildable now.\n");
374 return $lock->mark_failed;
377 $lock->log("According dependency solver, this package is " .
378 "rebuildable now.\n");
379 return $lock->mark_done;
382 # Get binary RPM packages for the source package.
383 # If first argument is:
384 # 'koji' download them from Koji,
385 # 'local' from local build,
386 # 'mock' from mock result directory.
387 # Requires `clone'. Sould be called after `build'.
388 # Return true on success.
389 sub binaryrpm {
390 my ($self, $mode) = @_;
391 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
392 state => 'rpms');
393 if ($lock->is_done) { return 1; }
395 if (!replace_directory($lock, $self->rpmdir)) {
396 return $lock->mark_failed;
399 # TODO: Get current architecture by rpmGetArchInfo() from librpm
400 my @archs = qw(x86_64 noarch);
401 if ($mode eq 'koji') {
402 $lock->log("Getting binary RPM packages from Koji:\n");
404 my $nevra = $self->get_nevra_from_git($lock);
405 if (! defined $nevra) {
406 return $lock->mark_failed;
409 # TODO: Get all archs, remove SRPM
410 if (!$lock->do($self->rpmdir, 'koji', 'download-build',
411 (map {'--arch=' . $_ } @archs), $nevra)) {
412 $lock->log("Could get binary RPM packages for `" . $nevra . "'\n");
413 return $lock->mark_failed;
415 } elsif ($mode eq 'local') {
416 $lock->log("Getting binary RPM packages from local build:\n");
418 my @rpms = map { glob(File::Spec->catfile($self->repodir, $_,
419 '*.rpm')) } @archs;
420 if ($#rpms < 0) {
421 $lock->log("No binary RPM packages found under `" .
422 $self->repodir . "'\n");
423 return $lock->mark_failed;
426 if (!copy_files_into_directory($lock, $self->rpmdir, @rpms)) {
427 return $lock->mark_failed;
429 } elsif ($mode eq 'mock') {
430 $lock->log("Getting binary RPM packages from mock build:\n");
432 my @rpms = map { glob(File::Spec->catfile($self->mockdir,
433 ('*.' . $_ . '.rpm'))) } @archs;
434 if ($#rpms < 0) {
435 $lock->log("No binary RPM packages found under `" .
436 $self->mockdir . "'\n");
437 return $lock->mark_failed;
440 if (!copy_files_into_directory($lock, $self->rpmdir, @rpms)) {
441 return $lock->mark_failed;
443 } else {
444 $lock->log("Could get binary RPM packages for `" . $self->name .
445 "' source package because of unknown building mode `" . $mode .
446 "'\n");
447 return $lock->mark_failed;
450 return $lock->mark_done;
453 # Distill Requires from rebuilt binary packages and serialize them into file.
454 # Return true on success.
455 # Needs `rpms'.
456 sub storebinaryrequires {
457 my $self = shift;
458 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
459 state => 'runrequiresstore');
460 if ($lock->is_done) { return 1; }
462 my @rpms = glob(File::Spec->catfile($self->rpmdir, '*.rpm'));
463 if ($#rpms < 0) {
464 $lock->log("No binary RPM packages found in `" . $self->rpmdir
465 . "'\n");
466 return $lock->mark_failed;
469 my $allrequires = {};
470 for my $rpmname (@rpms) {
471 my $rpm = Fedora::Rebuild::RPM->new(name => $rpmname);
473 my ($requires, $envr) = $rpm->requires;
475 if (! defined $requires || ! defined $envr) {
476 $lock->log("Could not get run-time requires of RPM `" . $rpmname .
477 "': " . $@ ."\n");
478 return $lock->mark_failed;
480 $$allrequires{$envr} = $requires;
483 if (! $lock->nstorereference($allrequires, $self->runrequiresstore)) {
484 $lock->log("Could not store run-time requires of RPM `". $self->name .
485 "' into `" . $self->runrequiresstore . "' file: $@\n");
486 return $lock->mark_failed;
489 $lock->log(Data::Dumper::Dumper($allrequires));
490 return $lock->mark_done;
493 # Distill Provides from rebuilt binary packages and serialize them into file.
494 # Return true on success.
495 # Needs `rpms'.
496 sub storebinaryprovides {
497 my $self = shift;
498 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
499 state => 'providesstore');
500 if ($lock->is_done) { return 1; }
502 my @rpms = glob(File::Spec->catfile($self->rpmdir, '*.rpm'));
503 if ($#rpms < 0) {
504 $lock->log("No binary RPM packages found in `" . $self->rpmdir
505 . "'\n");
506 return $lock->mark_failed;
509 my $allprovides = {};
510 for my $rpmname (@rpms) {
511 my $rpm = Fedora::Rebuild::RPM->new(name => $rpmname);
513 my ($provides, $envr) = $rpm->provides;
515 if (! defined $provides || !defined $envr) {
516 $lock->log("Could not get provides of RPM `" . $rpmname . "': " .
517 $@ ."\n");
518 return $lock->mark_failed;
520 $$allprovides{$envr} = $provides;
523 if (! $lock->nstorereference($allprovides, $self->providesstore)) {
524 $lock->log("Could not store provides of RPM `". $self->name .
525 "' into `" . $self->providesstore . "' file: $@\n");
526 return $lock->mark_failed;
529 $lock->log(Data::Dumper::Dumper($allprovides));
530 return $lock->mark_done;
533 # Load run-time requires of already rebuilt binary packages from file.
534 # Return true on success.
535 # Needs `storebinaryrequires'.
536 sub loadbinaryrequires {
537 my $self = shift;
538 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
539 state => 'runrequires');
540 if ($lock->is_done) { return 1; }
542 my $runrequires = $lock->retrievereference($self->runrequiresstore);
543 if (! $runrequires) {
544 $lock->log("Could not load run-time requires of `". $self->name .
545 "' package from `" . $self->runrequiresstore . "' file: $@\n");
546 return $lock->mark_failed;
548 $self->runrequires(shared_clone($runrequires));
550 $lock->log(Data::Dumper::Dumper($self->runrequires));
551 return $lock->mark_done;
554 # Load provides of already rebuilt binary packages from file.
555 # Return true on success.
556 # Needs `storebinaryprovides'.
557 sub loadbinaryprovides {
558 my $self = shift;
559 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
560 state => 'provides');
561 if ($lock->is_done) { return 1; }
563 my $provides = $lock->retrievereference($self->providesstore);
564 if (! $provides) {
565 $lock->log("Could not load provides of `". $self->name .
566 "' package from `" . $self->providesstore . "' file: $@\n");
567 return $lock->mark_failed;
569 $self->provides(shared_clone($provides));
571 $lock->log(Data::Dumper::Dumper($self->provides));
572 return $lock->mark_done;
575 # Increase package revision if not yet done. Commit change if first argument
576 # is "koji".
577 # Return true on success.
578 # Needs `clone'.
579 sub update {
580 my ($self, $mode) = @_;
581 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
582 state => 'update');
583 if ($lock->is_done) { return 1; }
585 # Reset git repository
586 if (!$lock->do($self->repodir, 'git', 'reset', '--hard',
587 'origin/' . $self->branch)) {
588 $lock->log("Could not reset git repository in `" . $self->repodir .
589 "'.\n");
590 return $lock->mark_failed;
593 # Pull git repository
594 if (!$lock->do($self->repodir, 'git', 'pull')) {
595 $lock->log("Could not pull git repository in `" . $self->repodir .
596 "'.\n");
597 return $lock->mark_failed;
600 # Increase revision number
601 my $specfile = File::Spec->catfile($self->repodir, $self->name . '.spec');
602 if (!$lock->do(undef, 'rpmdev-bumpspec', '-c', $self->message, $specfile)) {
603 $lock->log("Could not increase revison number in `" . $specfile .
604 "' specfile.\n");
605 return $lock->mark_failed;
608 # Commit changes
609 if (!$lock->do($self->repodir, 'git', 'commit', '-a',
610 '-m', $self->message)) {
611 $lock->log("Could not commit changes into git repository `" .
612 $self->repodir . "'.\n");
613 return $lock->mark_failed;
616 if ($mode eq 'koji') {
617 # Push changes
618 if (!$lock->do($self->repodir, 'git', 'push')) {
619 $lock->log("Could not push changes from repository `" .
620 $self->repodir . "' to server.\n");
621 return $lock->mark_failed;
623 } else {
624 $lock->log("Not pushing changes because of local build mode.\n");
627 return $lock->mark_done;
631 # Submit package for building into Koji and store task ID.
632 # This is pointless in local build mode.
633 # Requires `clone'. Sould be called after `update'.
634 # Return true on success.
635 sub submitbuild {
636 my ($self) = @_;
637 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
638 state => 'submitbuild');
639 if ($lock->is_done) { return 1; }
641 # Get NEVRA of intended build
642 my $nevra = $self->get_nevra_from_git($lock);
643 if (! defined $nevra) {
644 return $lock->mark_failed;
647 # Check the build is not already in Koji (e.g. by concurrent packager
648 # or after this program restart) but do not conclude anything.
649 my $buildinfo;
650 if (!$lock->dooutput($self->repodir, \$buildinfo, 'koji', 'buildinfo',
651 $nevra)) {
652 $lock->log("Could not ask Koji for `" . $nevra . "' status " .
653 "before submitting new build.\n");
654 return $lock->mark_failed;
656 if ($buildinfo =~ /No such build/m) {
657 $lock->log("Package not yet submitted for building as expected.\n");
658 } else {
659 # Get task ID of already building package
660 if ($buildinfo =~ /Task:\s*(\d+)/m) {
661 # TODO: We could compare task target and consider as submitted if
662 # equaled to intended target.
663 my $task_id = $1;
664 $lock->log("Package `$nevra' already submitted as task " .
665 "`$task_id'. Previous build failed or somebody builds the " .
666 "package concurrently.\n");
667 } else {
668 $lock->log("Package `$nevra' already in Koji, but task ID " .
669 "could not been determined.\n");
671 $lock->log("Re-submitting the package.\n")
674 # Submit into Koji
675 my $task_id;
676 if (!$lock->dooutput($self->repodir, \$task_id, 'fedpkg', 'build',
677 '--nowait', '--target', $self->target)) {
678 $lock->log("Could not submit `" . $nevra . "' into Koji.\n");
679 return $lock->mark_failed;
681 if (not $task_id =~ /Created task:\s*(\d+)/) {
682 $lock->log("Could not parse Koji task ID for `$nevra' build\n");
683 return $lock->mark_failed;
685 $task_id = $1;
687 # Store task ID
688 if (! $lock->nstorereference(\$task_id, $self->taskstore)) {
689 $lock->log("Could not store task ID `" . $task_id . "' of `" . $nevra .
690 "' package into `" . $self->taskstore . "' file: $@\n");
691 return $lock->mark_failed;
692 } else {
693 $lock->log("Task ID `" . $task_id . "' stored into `" .
694 $self->taskstore . "' file sucessfully.\n");
697 return $lock->mark_done;
701 # If first argument is:
702 # "koji" wait for package build in Koji,
703 # "local" build locally using fedpkg.
704 # "mock" build locally using mock.
705 # Requires `clone'. Sould be called after `update' or `submitbuild'.
706 # Return true on success.
707 sub build {
708 my ($self, $mode) = @_;
709 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
710 state => 'build');
711 if ($lock->is_done) { return 1; }
713 my $nevra = $self->get_nevra_from_git($lock);
714 if (! defined $nevra) {
715 return $lock->mark_failed;
718 if ($mode eq 'local') {
719 if (!$lock->do($self->repodir, 'fedpkg', 'local')) {
720 $lock->log("Could not build `" . $nevra . "' locally.\n");
721 return $lock->mark_failed;
723 } elsif ($mode eq 'koji') {
724 # Retrieve task ID of submitted build
725 my $task_id = $lock->retrievereference($self->taskstore);
726 if (! $task_id) {
727 $lock->log("Could not load task ID for `". $nevra .
728 "' build from `" . $self->taskstore . "' file: $@\n");
729 return $lock->mark_failed;
731 $task_id = $$task_id;
733 # Wait for build task result
734 # TODO: How to recognize the process died for other reason
735 # than build failure?
736 if (!$lock->do($self->repodir, 'koji', 'watch-task', $task_id)) {
737 $lock->log("Could not get status of Koji task `" . $task_id .
738 "' for `$nevra' build, or the task failed.\n");
739 return $lock->mark_failed;
741 } elsif ($mode eq 'mock') {
742 my $srpm_name = $self->get_srpm_name($lock);
743 if (! defined $srpm_name) {
744 return $lock->mark_failed;
746 if (!replace_directory($lock, $self->mockdir)) {
747 return $lock->mark_failed;
749 # FIXME: Specify mock config to match selected dist and branch
750 # (mock --configdir)
751 # FIXME: Inject already rebuilt packages
752 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
753 '--rebuild', $srpm_name)) {
754 $lock->log("Could not build `" . $nevra . "' in mock.\n");
755 return $lock->mark_failed;
757 } else {
758 $lock->log("Could not build `" . $nevra .
759 "' because of unknown building mode `" . $mode . "'.\n");
760 return $lock->mark_failed;
763 return $lock->mark_done;
767 # Waits for build root rotation. Just built package will be available for
768 # other packages at build time after returning from this fuction.
769 # If first argument is "koji", it will wait for the Koji rotation.
770 # If first argument is "mock", it will create yum repository in directory with
771 # binary packages.
772 # If first argument is "local", this function is void,
773 # Requires `update'. Sould be called after `build'.
774 # Return true on success.
775 sub dowaitforbuildroot {
776 my ($self, $mode) = @_;
777 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
778 state => 'rotate');
779 if ($lock->is_done) { return 1; }
781 my $nevra = $self->get_nevra_from_git($lock);
782 if (! defined $nevra) {
783 return $lock->mark_failed;
786 if ($mode eq 'koji') {
787 if (!$lock->do($self->repodir, 'koji', 'wait-repo',
788 '--build=' . $nevra, '--target', $self->target)) {
789 $lock->log("Koji does not contain `" . $nevra .
790 "' package in build root for `" . $self->target .
791 "' build target yet.\n");
792 return $lock->mark_failed;
794 } elsif ($mode eq 'mock') {
795 $lock->log("`" . $nevra .
796 "' built in mock, creating yum repository...\n");
797 if (!$lock->do($self->rpmdir, 'createrepo', '.')) {
798 $lock->log("Could not create yum repository for `" . $nevra .
799 "' package.\n");
800 return $lock->mark_failed;
802 $lock->log("`" . $nevra .
803 "' yum repository created successfully.\n");
804 } else {
805 $lock->log("`" . $nevra .
806 "' built locally, not waiting on Koji rotation.\n");
809 return $lock->mark_done;
812 # Set hash of build-time dependencies (requires attribute).
813 # Return undef in case of failure.
814 sub get_buildrequires {
815 my $self = shift;
816 my $ok;
817 print "Getting BuildRequires for `" . $self->name . "'...\n";
819 # Procede all steps, each must be re-doable
820 $ok = $self->clone;
821 $ok = $self->srpm if $ok;
822 $ok = $self->storebuildrequires if $ok;
823 $ok = $self->buildrequires if $ok;
825 #...;
827 if ($ok) {
828 print "BuildRequires for `" . $self->name .
829 "' package distilled successfully.\n";
830 return 1;
831 } else {
832 print "Could not get BuildRequires for `" . $self->name .
833 "' package.\n";
834 return undef;
838 # Set hash of run-time requires (Requires attribute).
839 # Return true on success, undef in case of failure.
840 # XXX: Requires `runrequiresstore'
841 sub get_binaryrequires {
842 my $self = shift;
843 my $ok;
844 print "Getting run-time requires for `" . $self->name . "'...\n";
846 $ok = $self->storebinaryrequires;
847 $ok = $self->loadbinaryrequires if $ok;
849 if ($ok) {
850 print "Run-time requires for `" . $self->name .
851 "' package distilled successfully.\n";
852 return 1;
853 } else {
854 print "Could not get run-time requires for `" . $self->name .
855 "' package.\n";
856 return undef;
860 # Set hash of run-time provides (provides attribute).
861 # Return true on success, undef in case of failure.
862 # XXX: Requires `providesstore'
863 sub get_binaryprovides {
864 my $self = shift;
865 my $ok;
866 print "Getting binary provides for `" . $self->name . "'...\n";
868 $ok = $self->storebinaryprovides;
869 $ok = $self->loadbinaryprovides if $ok;
871 if ($ok) {
872 print "Provides for `" . $self->name .
873 "' package distilled successfully.\n";
874 return 1;
875 } else {
876 print "Could not get Provides for `" . $self->name . "' package.\n";
877 return undef;
881 # Set hash of run-time requires (Requires attribute) and provides
882 # (provides attribute). Common wrapper for getbinaryrequires() and
883 # getbinaryprovides().
884 # Return true on success, undef in case of failure.
885 # XXX: Requires `runrequiresstore' and `providesstore'
886 sub get_binarydependencies {
887 my $self = shift;
888 my $ok;
889 print "Getting binary dependencies for `" . $self->name . "'...\n";
891 $ok = $self->get_binaryrequires;
892 $ok = $self->get_binaryprovides if $ok;
894 if ($ok) {
895 print "Binary dependencies for `" . $self->name .
896 "' package distilled successfully.\n";
897 return 1;
898 } else {
899 print "Could not get binary dependencies for `" . $self->name .
900 "' package.\n";
901 return undef;
905 # Rebuild a package without waiting for propagation to next build root.
906 # First argument defines build mode:
907 # "koji" publish commit and build in Koji,
908 # "local" build locally without pushing commits to server,
909 # "mock" build in mock without pushing commits to server.
910 sub rebuild {
911 my ($self, $mode) = @_;
912 my $ok;
913 print "Rebuilding `" . $self->name . "'...\n";
915 # Procede all steps, each must be re-doable
916 $ok = $self->clone;
917 $ok = $self->update($mode) if $ok;
918 $ok = $self->submitbuild if ($ok and $mode eq 'koji');
919 $ok = $self->build($mode) if $ok;
920 $ok = $self->binaryrpm($mode) if $ok;
921 $ok = $self->get_binaryprovides if $ok;
922 $ok = $self->get_binaryrequires if $ok;
924 if ($ok) {
925 print "`" . $self->name . "' rebuild finished successfully.\n";
926 } else {
927 print "`" . $self->name . "' rebuild failed.\n";
929 return $ok;
932 # Wait for the package propagated into new build root.
933 # If first argument is "koji", waits for Koji build root rotation,
934 # Otherwise waits locally (no-op).
935 # XXX: Requires `update', should be called after rebuilding package.
936 sub waitforbuildroot {
937 my ($self, $mode) = @_;
938 my $ok;
939 print "Waiting for `" . $self->name . "' to get to build root...\n";
941 $ok = $self->dowaitforbuildroot($mode);
943 if ($ok) {
944 print "`" . $self->name . "' propagated successfully.\n";
945 } else {
946 print "`" . $self->name . "' propagation failed.\n";
948 return $ok;