dist-rawhide is known as rawhide by koji now
[Fedora-Rebuild.git] / lib / Fedora / Rebuild / Package.pm
blob8c5d12d58c06f7402187a69552923244b8c8f282
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 "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 # Return list of binary RPM files relative to current working directory.
454 # XXX: Should be called after colleting the files from build process by
455 # binaryrpm().
456 sub listbinaryrpmfiles {
457 my $self = shift;
458 return (glob(File::Spec->catfile($self->rpmdir, '*.rpm')));
461 # Distill Requires from rebuilt binary packages and serialize them into file.
462 # Return true on success.
463 # Needs `rpms'.
464 sub storebinaryrequires {
465 my $self = shift;
466 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
467 state => 'runrequiresstore');
468 if ($lock->is_done) { return 1; }
470 my @rpms = glob(File::Spec->catfile($self->rpmdir, '*.rpm'));
471 if ($#rpms < 0) {
472 $lock->log("No binary RPM packages found in `" . $self->rpmdir
473 . "'\n");
474 return $lock->mark_failed;
477 my $allrequires = {};
478 for my $rpmname (@rpms) {
479 my $rpm = Fedora::Rebuild::RPM->new(name => $rpmname);
481 my ($requires, $envr) = $rpm->requires;
483 if (! defined $requires || ! defined $envr) {
484 $lock->log("Could not get run-time requires of RPM `" . $rpmname .
485 "': " . $@ ."\n");
486 return $lock->mark_failed;
488 $$allrequires{$envr} = $requires;
491 if (! $lock->nstorereference($allrequires, $self->runrequiresstore)) {
492 $lock->log("Could not store run-time requires of RPM `". $self->name .
493 "' into `" . $self->runrequiresstore . "' file: $@\n");
494 return $lock->mark_failed;
497 $lock->log(Data::Dumper::Dumper($allrequires));
498 return $lock->mark_done;
501 # Distill Provides from rebuilt binary packages and serialize them into file.
502 # Return true on success.
503 # Needs `rpms'.
504 sub storebinaryprovides {
505 my $self = shift;
506 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
507 state => 'providesstore');
508 if ($lock->is_done) { return 1; }
510 my @rpms = glob(File::Spec->catfile($self->rpmdir, '*.rpm'));
511 if ($#rpms < 0) {
512 $lock->log("No binary RPM packages found in `" . $self->rpmdir
513 . "'\n");
514 return $lock->mark_failed;
517 my $allprovides = {};
518 for my $rpmname (@rpms) {
519 my $rpm = Fedora::Rebuild::RPM->new(name => $rpmname);
521 my ($provides, $envr) = $rpm->provides;
523 if (! defined $provides || !defined $envr) {
524 $lock->log("Could not get provides of RPM `" . $rpmname . "': " .
525 $@ ."\n");
526 return $lock->mark_failed;
528 $$allprovides{$envr} = $provides;
531 if (! $lock->nstorereference($allprovides, $self->providesstore)) {
532 $lock->log("Could not store provides of RPM `". $self->name .
533 "' into `" . $self->providesstore . "' file: $@\n");
534 return $lock->mark_failed;
537 $lock->log(Data::Dumper::Dumper($allprovides));
538 return $lock->mark_done;
541 # Load run-time requires of already rebuilt binary packages from file.
542 # Return true on success.
543 # Needs `storebinaryrequires'.
544 sub loadbinaryrequires {
545 my $self = shift;
546 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
547 state => 'runrequires');
548 if ($lock->is_done) { return 1; }
550 my $runrequires = $lock->retrievereference($self->runrequiresstore);
551 if (! $runrequires) {
552 $lock->log("Could not load run-time requires of `". $self->name .
553 "' package from `" . $self->runrequiresstore . "' file: $@\n");
554 return $lock->mark_failed;
556 $self->runrequires(shared_clone($runrequires));
558 $lock->log(Data::Dumper::Dumper($self->runrequires));
559 return $lock->mark_done;
562 # Load provides of already rebuilt binary packages from file.
563 # Return true on success.
564 # Needs `storebinaryprovides'.
565 sub loadbinaryprovides {
566 my $self = shift;
567 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
568 state => 'provides');
569 if ($lock->is_done) { return 1; }
571 my $provides = $lock->retrievereference($self->providesstore);
572 if (! $provides) {
573 $lock->log("Could not load provides of `". $self->name .
574 "' package from `" . $self->providesstore . "' file: $@\n");
575 return $lock->mark_failed;
577 $self->provides(shared_clone($provides));
579 $lock->log(Data::Dumper::Dumper($self->provides));
580 return $lock->mark_done;
583 # Increase package revision if not yet done. Commit change if first argument
584 # is "koji".
585 # Return true on success.
586 # Needs `clone'.
587 sub update {
588 my ($self, $mode) = @_;
589 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
590 state => 'update');
591 if ($lock->is_done) { return 1; }
593 # Reset git repository
594 if (!$lock->do($self->repodir, 'git', 'reset', '--hard',
595 'origin/' . $self->branch)) {
596 $lock->log("Could not reset git repository in `" . $self->repodir .
597 "'.\n");
598 return $lock->mark_failed;
601 # Pull git repository
602 if (!$lock->do($self->repodir, 'git', 'pull')) {
603 $lock->log("Could not pull git repository in `" . $self->repodir .
604 "'.\n");
605 return $lock->mark_failed;
608 # Increase revision number
609 my $specfile = File::Spec->catfile($self->repodir, $self->name . '.spec');
610 if (!$lock->do(undef, 'rpmdev-bumpspec', '-c', $self->message, $specfile)) {
611 $lock->log("Could not increase revison number in `" . $specfile .
612 "' specfile.\n");
613 return $lock->mark_failed;
616 # Commit changes
617 if (!$lock->do($self->repodir, 'git', 'commit', '-a',
618 '-m', $self->message)) {
619 $lock->log("Could not commit changes into git repository `" .
620 $self->repodir . "'.\n");
621 return $lock->mark_failed;
624 if ($mode eq 'koji') {
625 # Push changes
626 if (!$lock->do($self->repodir, 'git', 'push')) {
627 $lock->log("Could not push changes from repository `" .
628 $self->repodir . "' to server.\n");
629 return $lock->mark_failed;
631 } else {
632 $lock->log("Not pushing changes because of local build mode.\n");
635 return $lock->mark_done;
639 # Submit package for building into Koji and store task ID.
640 # This is pointless in local build mode.
641 # Requires `clone'. Sould be called after `update'.
642 # Return true on success.
643 sub submitbuild {
644 my ($self) = @_;
645 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
646 state => 'submitbuild');
647 if ($lock->is_done) { return 1; }
649 # Get NEVRA of intended build
650 my $nevra = $self->get_nevra_from_git($lock);
651 if (! defined $nevra) {
652 return $lock->mark_failed;
655 # Check the build is not already in Koji (e.g. by concurrent packager
656 # or after this program restart) but do not conclude anything.
657 my $buildinfo;
658 if (!$lock->dooutput($self->repodir, \$buildinfo, 'koji', 'buildinfo',
659 $nevra)) {
660 $lock->log("Could not ask Koji for `" . $nevra . "' status " .
661 "before submitting new build.\n");
662 return $lock->mark_failed;
664 if ($buildinfo =~ /No such build/m) {
665 $lock->log("Package not yet submitted for building as expected.\n");
666 } else {
667 # Get task ID of already building package
668 if ($buildinfo =~ /Task:\s*(\d+)/m) {
669 # TODO: We could compare task target and consider as submitted if
670 # equaled to intended target.
671 my $task_id = $1;
672 $lock->log("Package `$nevra' already submitted as task " .
673 "`$task_id'. Previous build failed or somebody builds the " .
674 "package concurrently.\n");
675 } else {
676 $lock->log("Package `$nevra' already in Koji, but task ID " .
677 "could not been determined.\n");
679 $lock->log("Re-submitting the package.\n")
682 # Submit into Koji
683 my $task_id;
684 if (!$lock->dooutput($self->repodir, \$task_id, 'fedpkg', 'build',
685 '--nowait', '--target', $self->target)) {
686 $lock->log("Could not submit `" . $nevra . "' into Koji.\n");
687 return $lock->mark_failed;
689 if (not $task_id =~ /Created task:\s*(\d+)/) {
690 $lock->log("Could not parse Koji task ID for `$nevra' build\n");
691 return $lock->mark_failed;
693 $task_id = $1;
695 # Store task ID
696 if (! $lock->nstorereference(\$task_id, $self->taskstore)) {
697 $lock->log("Could not store task ID `" . $task_id . "' of `" . $nevra .
698 "' package into `" . $self->taskstore . "' file: $@\n");
699 return $lock->mark_failed;
700 } else {
701 $lock->log("Task ID `" . $task_id . "' stored into `" .
702 $self->taskstore . "' file sucessfully.\n");
705 return $lock->mark_done;
709 # If first argument is:
710 # "koji" wait for package build in Koji,
711 # "local" build locally using fedpkg.
712 # "mock" build locally using mock.
713 # Second argument is reference to array of repository URLs to use as package
714 # base when building the package.
715 # Requires `clone'. Sould be called after `update' or `submitbuild'.
716 # Return true on success.
717 sub build {
718 my ($self, $mode, $repositories) = @_;
719 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
720 state => 'build');
721 if ($lock->is_done) { return 1; }
723 my $nevra = $self->get_nevra_from_git($lock);
724 if (! defined $nevra) {
725 return $lock->mark_failed;
728 if ($mode eq 'local') {
729 if (!$lock->do($self->repodir, 'fedpkg', 'local')) {
730 $lock->log("Could not build `" . $nevra . "' locally.\n");
731 return $lock->mark_failed;
733 } elsif ($mode eq 'koji') {
734 # Retrieve task ID of submitted build
735 my $task_id = $lock->retrievereference($self->taskstore);
736 if (! $task_id) {
737 $lock->log("Could not load task ID for `". $nevra .
738 "' build from `" . $self->taskstore . "' file: $@\n");
739 return $lock->mark_failed;
741 $task_id = $$task_id;
743 # Wait for build task result
744 # TODO: How to recognize the process died for other reason
745 # than build failure?
746 if (!$lock->do($self->repodir, 'koji', 'watch-task', $task_id)) {
747 $lock->log("Could not get status of Koji task `" . $task_id .
748 "' for `$nevra' build, or the task failed.\n");
749 return $lock->mark_failed;
751 } elsif ($mode eq 'mock') {
752 my $srpm_name = $self->get_srpm_name($lock);
753 if (! defined $srpm_name) {
754 return $lock->mark_failed;
756 if (!replace_directory($lock, $self->mockdir)) {
757 return $lock->mark_failed;
759 # FIXME: Specify mock config to match selected dist and branch
760 # (mock --configdir)
761 # FIXME: Inject already rebuilt packages
762 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
763 '--rebuild', $srpm_name)) {
764 $lock->log("Could not build `" . $nevra . "' in mock.\n");
765 return $lock->mark_failed;
767 } else {
768 $lock->log("Could not build `" . $nevra .
769 "' because of unknown building mode `" . $mode . "'.\n");
770 return $lock->mark_failed;
773 return $lock->mark_done;
777 # Waits for build root rotation. Just built package will be available for
778 # other packages at build time after returning from this fuction.
779 # If first argument is "koji", it will wait for the Koji rotation.
780 # If first argument is "mock", it will create yum repository in directory with
781 # binary packages.
782 # If first argument is "local", this function is void,
783 # Requires `update'. Sould be called after `build'.
784 # Return true on success.
785 sub dowaitforbuildroot {
786 my ($self, $mode) = @_;
787 my $lock = Fedora::Rebuild::Package::StateLock->new(package => $self,
788 state => 'rotate');
789 if ($lock->is_done) { return 1; }
791 my $nevra = $self->get_nevra_from_git($lock);
792 if (! defined $nevra) {
793 return $lock->mark_failed;
796 if ($mode eq 'koji') {
797 if (!$lock->do($self->repodir, 'koji', 'wait-repo',
798 '--build=' . $nevra, '--target', $self->target)) {
799 $lock->log("Koji does not contain `" . $nevra .
800 "' package in build root for `" . $self->target .
801 "' build target yet.\n");
802 return $lock->mark_failed;
804 } elsif ($mode eq 'mock') {
805 $lock->log("`" . $nevra .
806 "' built in mock, creating yum repository...\n");
807 if (!$lock->do($self->rpmdir, 'createrepo', '.')) {
808 $lock->log("Could not create yum repository for `" . $nevra .
809 "' package.\n");
810 return $lock->mark_failed;
812 $lock->log("`" . $nevra .
813 "' yum repository created successfully.\n");
814 } else {
815 $lock->log("`" . $nevra .
816 "' built locally, not waiting on Koji rotation.\n");
819 return $lock->mark_done;
822 # Set hash of build-time dependencies (requires attribute).
823 # Return undef in case of failure.
824 sub get_buildrequires {
825 my $self = shift;
826 my $ok;
827 print "Getting BuildRequires for `" . $self->name . "'...\n";
829 # Procede all steps, each must be re-doable
830 $ok = $self->clone;
831 $ok = $self->srpm if $ok;
832 $ok = $self->storebuildrequires if $ok;
833 $ok = $self->buildrequires if $ok;
835 #...;
837 if ($ok) {
838 print "BuildRequires for `" . $self->name .
839 "' package distilled successfully.\n";
840 return 1;
841 } else {
842 print "Could not get BuildRequires for `" . $self->name .
843 "' package.\n";
844 return undef;
848 # Set hash of run-time requires (Requires attribute).
849 # Return true on success, undef in case of failure.
850 # XXX: Requires `runrequiresstore'
851 sub get_binaryrequires {
852 my $self = shift;
853 my $ok;
854 print "Getting run-time requires for `" . $self->name . "'...\n";
856 $ok = $self->storebinaryrequires;
857 $ok = $self->loadbinaryrequires if $ok;
859 if ($ok) {
860 print "Run-time requires for `" . $self->name .
861 "' package distilled successfully.\n";
862 return 1;
863 } else {
864 print "Could not get run-time requires for `" . $self->name .
865 "' package.\n";
866 return undef;
870 # Set hash of run-time provides (provides attribute).
871 # Return true on success, undef in case of failure.
872 # XXX: Requires `providesstore'
873 sub get_binaryprovides {
874 my $self = shift;
875 my $ok;
876 print "Getting binary provides for `" . $self->name . "'...\n";
878 $ok = $self->storebinaryprovides;
879 $ok = $self->loadbinaryprovides if $ok;
881 if ($ok) {
882 print "Provides for `" . $self->name .
883 "' package distilled successfully.\n";
884 return 1;
885 } else {
886 print "Could not get Provides for `" . $self->name . "' package.\n";
887 return undef;
891 # Set hash of run-time requires (Requires attribute) and provides
892 # (provides attribute). Common wrapper for getbinaryrequires() and
893 # getbinaryprovides().
894 # Return true on success, undef in case of failure.
895 # XXX: Requires `runrequiresstore' and `providesstore'
896 sub get_binarydependencies {
897 my $self = shift;
898 my $ok;
899 print "Getting binary dependencies for `" . $self->name . "'...\n";
901 $ok = $self->get_binaryrequires;
902 $ok = $self->get_binaryprovides if $ok;
904 if ($ok) {
905 print "Binary dependencies for `" . $self->name .
906 "' package distilled successfully.\n";
907 return 1;
908 } else {
909 print "Could not get binary dependencies for `" . $self->name .
910 "' package.\n";
911 return undef;
915 # Rebuild a package without waiting for propagation to next build root.
916 # First argument defines build mode:
917 # "koji" publish commit and build in Koji,
918 # "local" build locally without pushing commits to server,
919 # "mock" build in mock without pushing commits to server.
920 # Second argument is reference to array of repository URLs to use as package
921 # base when building the package.
922 sub rebuild {
923 my ($self, $mode, $repositories) = @_;
924 my $ok;
925 print "Rebuilding `" . $self->name . "'...\n";
927 # Proceed all steps, each must be re-doable
928 $ok = $self->clone;
929 $ok = $self->update($mode) if $ok;
930 $ok = $self->submitbuild if ($ok and $mode eq 'koji');
931 $ok = $self->build($mode, $repositories) if $ok;
932 $ok = $self->binaryrpm($mode) if $ok;
933 $ok = $self->get_binaryprovides if $ok;
934 $ok = $self->get_binaryrequires if $ok;
936 if ($ok) {
937 print "`" . $self->name . "' rebuild finished successfully.\n";
938 } else {
939 print "`" . $self->name . "' rebuild failed.\n";
941 return $ok;
944 # Wait for the package propagated into new build root.
945 # If first argument is "koji", waits for Koji build root rotation,
946 # Otherwise waits locally (no-op).
947 # XXX: Requires `update', should be called after rebuilding package.
948 sub waitforbuildroot {
949 my ($self, $mode) = @_;
950 my $ok;
951 print "Waiting for `" . $self->name . "' to get to build root...\n";
953 $ok = $self->dowaitforbuildroot($mode);
955 if ($ok) {
956 print "`" . $self->name . "' propagated successfully.\n";
957 } else {
958 print "`" . $self->name . "' propagation failed.\n";
960 return $ok;