1 package Fedora
::Rebuild
::Package
;
9 use Fedora
::Rebuild
::Package
::StateLock
;
10 use Fedora
::Rebuild
::RPM
;
11 use Fedora
::Rebuild
::Mock
;
15 use version
0.77; our $VERSION = version
->declare("v0.12.1");
17 has
'name' => (is
=> 'ro', isa
=> 'Str', required
=> 1);
18 # Build-time dependencies
19 has
'requires' => (is
=> 'rw', isa
=> 'HashRef', lazy_build
=> 1,
21 # Run-time dependencies hashed by binary package ENVR.
22 has
'runrequires' => (is
=> 'rw', isa
=> 'HashRef', lazy_build
=> 1,
24 # Run-time provides hashes by binary package ENVR.
25 has
'provides' => (is
=> 'rw', isa
=> 'HashRef', lazy_build
=> 1,
27 has
'workdir' => (is
=> 'ro', isa
=> 'Str', required
=> 1);
29 # "f14", "f15" etc. Use "rawhide" for latest one.
30 has
'dist' => (is
=> 'ro', isa
=> 'Str', required
=> 1);
31 # Fedpkg dist name identifier. This is required only if git branch name is not
32 # an offical one (e.g. a private branch).
33 # "f14", "f15" etc. Use "f18" for the latest one.
34 has
'pkgdist' => (is
=> 'ro', isa
=> 'Maybe[Str]', required
=> 0,
37 # "dist-f14", "dist-f15" etc. Use "rawhide" for latest one.
38 has
'target' => (is
=> 'ro', isa
=> 'Str', required
=> 1);
39 has
'message' => (is
=> 'ro', isa
=> 'Str', required
=> 1);
41 has
'packagedir' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
43 has
'repodir' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
45 has
'mockdir' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
47 has
'rpmdir' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
49 has
'requiresstore' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
51 has
'runrequiresstore' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
53 has
'providesstore' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
55 has
'taskstore' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
57 has
'branch' => (is
=> 'ro', isa
=> 'Str', lazy_build
=> 1,
60 # Make object shared between threads.
61 # XXX: Not all attributes are shared automatically.
65 return shared_clone
($class->$orig(@_));
68 # Clean package locks bound to attributes
71 for my $state ('buildrequires', 'runrequires', 'provides') {
72 Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
73 state => $state)->mark_failed;
77 # BuildRequires. Shared.
78 # Initialize to empty hash. Call get_buildrequires() to populate.
81 my $requires :shared
= shared_clone
({});
85 # Run-time Requires. Shared.
86 # Initialize to empty hash. Call get_runrequires() to populate.
87 sub _build_runrequires
{
89 my $runrequires :shared
= shared_clone
({});
93 # Run-time provides. Shared.
94 # Initialize to empty hash. Call rebuild() or get_binaryprovides() to populate.
97 my $provides :shared
= shared_clone
({});
101 sub _build_packagedir
{
103 my $directory = File
::Spec
->catfile($self->workdir, $self->name);
104 if (! -d
$directory) {
105 File
::Path
::Tiny
::mk
($directory) or
106 die "Could not create directory $directory: $!";
113 return File
::Spec
->catfile($self->packagedir, 'repository');
118 return File
::Spec
->catfile($self->packagedir, 'mock');
123 return File
::Spec
->catfile($self->packagedir, 'RPMS');
126 sub _build_requiresstore
{
128 return File
::Spec
->catfile($self->packagedir, 'buildrequires.store');
131 sub _build_runrequiresstore
{
133 return File
::Spec
->catfile($self->packagedir, 'runrequires.store');
136 sub _build_providesstore
{
138 return File
::Spec
->catfile($self->packagedir, 'provides.store');
141 sub _build_taskstore
{
143 return File
::Spec
->catfile($self->packagedir, 'task.store');
148 if ($self->dist eq 'rawhide') {
156 # Clones package repository and switch to proper branch if not yet done.
157 # First argument is anonymous clone boolean.
158 # Second argument is Fedora::Rebuild::Package::Config object.
159 # Return true on success.
161 my ($self, $anonymous, $build_config) = @_;
162 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
164 if ($lock->is_done) { return 1; }
166 # XXX: pyrpkg creates subdirectory named like package. The package name
167 # could clash with our file structure. Thus we need to clone into secure
168 # directory and move repository content to fixed name $self->repodir after
171 if (-d
$self->repodir) { File
::Path
::Tiny
::rm
($self->repodir); }
172 my $tempdir = File
::Spec
->catfile($self->packagedir, 'tmp');
173 my $temprepodir = File
::Spec
->catfile($tempdir, $self->name);
174 if (-d
$tempdir) { File
::Path
::Tiny
::rm
($tempdir); }
175 if (!File
::Path
::Tiny
::mk
($tempdir)) {
176 $lock->log("Could not create directory `" . $tempdir . "': $!\n");
177 return $lock->mark_failed;
180 if (!$lock->do($tempdir, $build_config->pyrpkg(), 'clone',
181 ($anonymous) ?
'-a': (), $self->name)) {
182 $lock->log("Could not clone `" . $self->name . "' repository.\n");
183 return $lock->mark_failed;
186 if (!rename($temprepodir, $self->repodir)) {
187 $lock->log("Could not move `" . $temprepodir . "' content to to `" .
188 $self->repodir . "'.\n");
189 return $lock->mark_failed;
191 File
::Path
::Tiny
::rm
($tempdir);
193 if (!$lock->do($self->repodir, $build_config->pyrpkg(),
194 'switch-branch', $self->branch)) {
195 $lock->log("Could not switch `" . $self->name .
196 "' repository to branch `" . $self->branch . "'.\n");
197 return $lock->mark_failed;
200 return $lock->mark_done;
204 # Increase package revision with proper changelog entry. Do not commit nor
206 # First argument is a state lock.
207 # XXX: The state is not marked as failed in case of error,
208 # Return true on success.
211 my ($self, $lock) = @_;
212 if (!defined $lock) {
216 # Reset git repository
217 if (!$lock->do($self->repodir, 'git', 'reset', '--hard',
218 'origin/' . $self->branch)) {
219 $lock->log("Could not reset git repository in `" . $self->repodir .
224 # Pull git repository
225 if (!$lock->do($self->repodir, 'git', 'pull')) {
226 $lock->log("Could not pull git repository in `" . $self->repodir .
231 # Increase revision number
232 my $specfile = File
::Spec
->catfile($self->repodir, $self->name . '.spec');
233 if (!$lock->do(undef, 'rpmdev-bumpspec', '-c', $self->message, $specfile)) {
234 $lock->log("Could not increase revison number in `" . $specfile .
243 # Remove all state locks except `clone'.
244 # XXX: The state is not marked as failed in case of error.
249 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
252 for my $state ('srpm', 'buildrequiresstore', 'update', 'is_rebuildable',
253 'submitbuild', 'build', 'rpms', 'providesstore', 'runrequiresstore',
254 'rotate', 'buildrequires', 'runrequires', 'provides') {
255 $lock->log("Removing state lock $state\n");
257 if ($state eq 'rotate') {
260 my $state_lock = Fedora
::Rebuild
::Package
::StateLock
->new(
261 package => $self, state => $state);
262 $state_lock->mark_failed;
263 $state_lock->remove_log;
266 $lock->log("States reset done.\n");
268 # The lock is for logging only
274 # Test if remote GIT repository HEAD differs from local clone. If the package
275 # has been changed in the remote repository, it will reset all package
276 # states up to (excluding) `clone'.
277 # Return false on failure, 1 for no change (repositories equals),
278 # 2 for detected change.
280 sub reset_remotly_updated
{
283 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
284 state => 'checkremote');
286 if (!-d
$self->repodir) {
287 $lock->log("Missing repository directory `" . $self->repodir . "\n");
288 return $lock->mark_failed;
291 # Expected output for "git show-ref origin/master"
292 # bb7f5790c2ef49087abe175e8b9560acce4ae15d refs/remotes/origin/master
294 my $branch_name = $self->branch;
295 if (!$lock->dooutput($self->repodir, \
$local_id, 'git', 'show-ref',
296 'origin/' . $self->branch)) {
297 $lock->log("Could not get HEAD of local origin branch.\n");
298 return $lock->mark_failed;
300 if (!defined $local_id or $local_id !~
301 s
|\A
(\S
+)\s
+refs
/remotes
/origin
/\Q
$branch_name\E
\n\z
|$1|) {
302 $lock->log("Could not parse local origin reference ID: `$local_id'\n");
303 return $lock->mark_failed;
306 # Expected output for "git ls-remote origin heads/master"
307 # 8d021a3956bbb1762ae52a04a5bc425a77c8a3ad refs/heads/master
308 # XXX: git ls-remote --heads ... handles master as a wildcard and returns
309 # entry for rc/master branch too.
311 if (!$lock->dooutput($self->repodir, \
$remote_id, 'git', 'ls-remote',
312 'origin', 'heads/' . $self->branch)) {
313 $lock->log("Could not get HEAD of remote origin branch.\n");
314 return $lock->mark_failed;
316 if (!defined $remote_id or $remote_id !~
317 s
|\A
(\S
+)\s
+refs
/heads/\Q
$branch_name\E
\n\z
|$1|) {
318 $lock->log("Could not parse remote origin reference ID: `$remote_id'\n");
319 return $lock->mark_failed;
322 my $changed = $local_id ne $remote_id;
324 $lock->log("Remote repository has changed.\n");
327 $lock->log("Remote repository has not changed.\n");
330 # The lock is for logging only
340 # Builds SRPM. Return true on success.
341 # If first argument is true, recreate SRPM forcefully.
342 # Second argument is Fedora::Rebuild::Package::Config object.
343 # Third argument is Mock object to use instead of fresh one (value undef).
344 # (This is safe only in single-threaded run. This is limitation of mock.)
347 my ($self, $force, $build_config, $mock) = @_;
348 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
350 if ($force) { $lock->remove_lock; }
351 if ($lock->is_done) { return 1; }
353 # We need do the update spec now to get the same NEVRA as in rebuild. We do
354 # update without pushing to prevent from repetitive NEVRA bumps in case of
356 if (!Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
357 state => 'update')->is_done) {
358 if (!$self->updatespec($lock)) {
359 $lock->log("Could not update spec file in order to create new " .
360 "SRPM for `" . $self->name . "' package.\n");
361 return $lock->mark_failed;
365 if ($build_config->mode() eq 'local') {
366 if (!$lock->do($self->repodir, $build_config->pyrpkg(),
367 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
369 $lock->log("Could not build SRPM for `" . $self->name .
370 "' package locally.\n");
371 return $lock->mark_failed;
373 } elsif ($build_config->mode() eq 'koji' or
374 $build_config->mode() eq 'mock') {
375 # Prepare spec file and sources
376 my $specname = File
::Spec
->catfile($self->repodir,
377 $self->name . '.spec');
378 if (!$lock->do($self->repodir, $build_config->pyrpkg(),
379 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
381 $lock->log("Could not download sources for `" . $self->name .
383 return $lock->mark_failed;
387 if (!replace_directory
($lock, $self->mockdir)) {
388 return $lock->mark_failed;
390 my ($mock_config_dir, $mock_config_root);
392 if (!defined $mock) {
393 $mock = Fedora
::Rebuild
::Mock
->new(
394 architecture
=> $build_config->architecture(),
395 repositories
=> $build_config->repositories(),
397 $build_config->mock_install_packages(),
399 $mock->make_configuration;
401 $mock_config_dir = $mock->config_dir;
402 $mock_config_root = $mock->config_root;
405 $lock->log("Could not configure mock: $@\n");
406 File
::Path
::Tiny
::rm
($mock_config_dir);
407 return $lock->mark_failed;
410 # Mock stopped to pruning _topdir and accumulates built SRPMs now
411 # and die on final check for number of SRPM files.
412 # Let clean it manually.
413 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
414 '--configdir', $mock_config_dir,
415 '--root', $mock_config_root,
416 '--no-clean', '--no-cleanup-after',
417 '--chroot', q
[rm
-r
"$(rpm -E '%{_topdir}')"])) {
418 $lock->log("Could not prune _topdir for `" . $self->name .
420 return $lock->mark_failed;
423 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
424 '--configdir', $mock_config_dir, '--root', $mock_config_root,
425 ($mock->shared) ?
('--no-clean', '--no-cleanup-after') : (),
427 '--spec', $specname, '--sources', $self->repodir)) {
428 $lock->log("Could not build SRPM for `" . $self->name .
430 if (!$mock->shared) {
431 File
::Path
::Tiny
::rm
($mock_config_dir);
433 return $lock->mark_failed;
435 if (!$mock->shared) {
436 File
::Path
::Tiny
::rm
($mock_config_dir);
439 # Move SRPM from mockdir to repodir
440 my @srpms = glob(File
::Spec
->catfile($self->mockdir, '*.src.rpm'));
442 $lock->log("No SRPM package found under `" .
443 $self->mockdir . "'\n");
444 return $lock->mark_failed;
447 $lock->log("More SRPM packages found under `" .
448 $self->mockdir . "'. This should not happen.\n");
449 return $lock->mark_failed;
451 if (!copy_files_into_directory
($lock, $self->repodir, @srpms)) {
452 return $lock->mark_failed;
455 $lock->log("Could not build SRPM for `" . $self->name .
456 "' because of unknown building mode `" . $build_config->mode() .
458 return $lock->mark_failed;
461 return $lock->mark_done;
464 # Get current package NEVRA from sources in repository.
465 # First argument is state lock where process of getting NEVRA including
466 # potential failure is logged.
467 # Second argument is Fedora::Rebuild::Package::Config object.
468 # XXX: The state is not marked as failed in case of error,
469 # Return NEVRA string or undef in case of error.
470 sub get_nevra_from_git
{
471 my ($self, $lock, $build_config) = @_;
474 if (!$lock->dooutput($self->repodir, \
$nevra, $build_config->pyrpkg(),
475 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
478 $lock->log("Could not get NEVRA from `" . $self->name .
479 "' git repository package.\n");
483 # Consider last line only becuase of bug in pyrpkg
484 # <https://bugzilla.redhat.com/show_bug.cgi?id=721389>.
485 my @lines = (split qr{$/}, $nevra);
491 # Get current package SRPM name.
492 # If the SRPM file does not exist, it will be re-created.
493 # First argument is state lock where process of building SRPM including
494 # potential failure is logged.
495 # Second argument is Fedora::Rebuild::Package::Config object. If mode value is:
496 # "local" build SRPM locally using pyrpkg,
497 # "koji" or "mock" build SRPM using mock.
498 # Third argument is Mock object to use instead of fresh one (value undef).
499 # XXX: The state is not marked as failed in case of error,
500 # Return SRPM file name string or undef in case of error.
502 my ($self, $lock, $build_config, $mock) = @_;
504 my $nevra = $self->get_nevra_from_git($lock, $build_config);
505 if (! defined $nevra) {
509 my $srpmname = File
::Spec
->catfile($self->repodir, $nevra . '.src.rpm');
510 if (! -f
$srpmname ) {
511 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
512 "trying to create SRPM again...\n");
513 if (!$self->srpm(1, $build_config, $mock)
515 $lock->log("`Could not recreate SRPM package '" . $srpmname .
523 # Remove a directory recursively, if it exists.
524 # First argument is the directory, second argument is lock to log errors into.
525 # Return true, false in case of error.
526 # XXX: This is not a method
527 sub remove_directory
{
528 my ($lock, $directory) = @_;
529 if (!File
::Path
::Tiny
::rm
($directory)) {
530 $lock->log("Could not remove directory `" . $directory . "': $!\n");
536 # Create a directory. If it exists, it will remove it before.
537 # First argument is the directory, second argument is lock to log errors into.
538 # Return true, false in case of error.
539 # XXX: This is not a method
540 sub replace_directory
{
541 my ($lock, $directory) = @_;
542 remove_directory
($lock, $directory);
543 if (!File
::Path
::Tiny
::mk
($directory)) {
544 $lock->log("Could not create directory `" . $directory . "': $!\n");
550 # Copy files into existing directory.
551 # First argument lock for logggin,
552 # second argument is destinatinon directory,
553 # The last is list of files to be copied.
554 # Return true in sucesss, false in case of error.
555 # XXX: This is not a method
556 sub copy_files_into_directory
{
557 my ($lock, $directory, @files) = @_;
558 for my $file (@files) {
560 if (!copy
($file, $directory)) {
561 $lock->log("Could not copy `". $file . "' into `". $directory .
565 $lock->log("`" . $file . "' copied into `" . $directory . "'\n");
570 # Destile BuildRequires from local SRPM and serialize them into file.
571 # First argument is Fedora::Rebuild::Package::Config object. If mode member is:
572 # "local" build SRPM locally using pyrpkg,
573 # "koji" or "mock" build SRPM using mock.
574 # Second argument is Mock object to use instead of fresh one (value undef).
575 # Return true on success.
577 # FIXME: does not work after cleaning clone or doing update.
578 sub storebuildrequires
{
579 my ($self, $build_config, $mock) = @_;
580 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
581 state => 'buildrequiresstore');
582 if ($lock->is_done) { return 1; }
584 my $nevra = $self->get_nevra_from_git($lock, $build_config);
585 if (! defined $nevra) {
586 return $lock->mark_failed;
589 my $srpmname = File
::Spec
->catfile($self->repodir, $nevra . '.src.rpm');
590 if (! -f
$srpmname ) {
591 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
592 "trying to create SRPM again...\n");
593 if (!$self->srpm(1, $build_config, $mock) || ! -f
$srpmname) {
594 $lock->log("`Could not recreate SRPM package '" . $srpmname .
596 return $lock->mark_failed;
600 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $srpmname);
601 my ($requires, $envra) = $rpm->requires;
602 if (! defined $requires) {
603 $lock->log("Could not get requires of SRPM `" . $srpmname . "': ". $@
605 return $lock->mark_failed;
608 if (! $lock->nstorereference($requires, $self->requiresstore)) {
609 $lock->log("Could not store requires of SRPM `". $srpmname .
610 "' into `" . $self->requiresstore . "' file: $@\n");
611 return $lock->mark_failed;
614 $lock->log(Data
::Dumper
::Dumper
($requires) . "\n");
615 return $lock->mark_done;
618 # Destile BuildRequires from local SRPM. Return true on success.
619 # Needs `buildrequiresstore'.
622 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
623 state => 'buildrequires');
624 if ($lock->is_done) { return 1; }
626 my $requires = $lock->retrievereference($self->requiresstore);
628 $lock->log("Could not load requires of `". $self->name .
629 "' package from `" . $self->requiresstore . "' file: $@\n");
630 return $lock->mark_failed;
632 $self->requires(shared_clone
($requires));
634 $lock->log(Data
::Dumper
::Dumper
($self->requires) . "\n");
635 return $lock->mark_done;
638 # Record verdict from dependency solver whether the package is rebuildable.
639 # This step is always redone.
640 sub log_is_rebuildable
{
641 my ($self, $is_rebuildable, $message) = @_;
642 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
643 state => 'is_rebuildable');
646 if (defined $message) {
647 $lock->log("Solver result for possibility of rebuilding SRPM for `" .
648 $self->name . "': $message\n");
650 if (! $is_rebuildable) {
651 $lock->log("According dependency solver, this package is not " .
652 "rebuildable now.\n");
653 return $lock->mark_failed;
656 $lock->log("According dependency solver, this package is " .
657 "rebuildable now.\n");
658 return $lock->mark_done;
661 # Get binary RPM packages for the source package.
662 # First argument is Fedora::Rebuild::Package::Config object. If mode member is:
663 # 'koji' download them from Koji,
664 # 'local' from local build,
665 # 'mock' from mock result directory.
666 # noarch packages are colletected automatically.
667 # Requires `clone'. Sould be called after `build'.
668 # Return true on success.
670 my ($self, $build_config) = @_;
671 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
673 if ($lock->is_done) { return 1; }
675 if (!replace_directory
($lock, $self->rpmdir)) {
676 return $lock->mark_failed;
679 my @archs = ($build_config->architecture(), 'noarch');
680 if ($build_config->mode eq 'koji') {
681 $lock->log("Getting binary RPM packages from Koji:\n");
683 my $nevra = $self->get_nevra_from_git($lock, $build_config);
684 if (! defined $nevra) {
685 return $lock->mark_failed;
688 # TODO: Get all archs, remove SRPM
689 if (!$lock->do($self->rpmdir, $build_config->koji(), 'download-build',
690 (map {'--arch=' . $_ } @archs), $nevra)) {
691 $lock->log("Could not get binary RPM packages for `" . $nevra .
693 return $lock->mark_failed;
695 } elsif ($build_config->mode() eq 'local') {
696 $lock->log("Getting binary RPM packages from local build:\n");
698 my @rpms = map { glob(File
::Spec
->catfile($self->repodir, $_,
701 $lock->log("No binary RPM packages found under `" .
702 $self->repodir . "'\n");
703 return $lock->mark_failed;
706 if (!copy_files_into_directory
($lock, $self->rpmdir, @rpms)) {
707 return $lock->mark_failed;
709 } elsif ($build_config->mode() eq 'mock') {
710 $lock->log("Getting binary RPM packages from mock build:\n");
712 my @rpms = map { glob(File
::Spec
->catfile($self->mockdir,
713 ('*.' . $_ . '.rpm'))) } @archs;
715 $lock->log("No binary RPM packages found under `" .
716 $self->mockdir . "'\n");
717 return $lock->mark_failed;
720 if (!copy_files_into_directory
($lock, $self->rpmdir, @rpms)) {
721 return $lock->mark_failed;
724 $lock->log("Could get binary RPM packages for `" . $self->name .
725 "' source package because of unknown building mode `" .
726 $build_config->mode() . "'\n");
727 return $lock->mark_failed;
730 return $lock->mark_done;
733 # Return list of binary RPM files relative to current working directory.
734 # XXX: Should be called after colleting the files from build process by
736 sub listbinaryrpmfiles
{
738 return (glob(File
::Spec
->catfile($self->rpmdir, '*.rpm')));
741 # Distill Requires from rebuilt binary packages and serialize them into file.
742 # Return true on success.
744 sub storebinaryrequires
{
746 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
747 state => 'runrequiresstore');
748 if ($lock->is_done) { return 1; }
750 my @rpms = glob(File
::Spec
->catfile($self->rpmdir, '*.rpm'));
752 $lock->log("No binary RPM packages found in `" . $self->rpmdir
754 return $lock->mark_failed;
757 my $allrequires = {};
758 for my $rpmname (@rpms) {
759 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $rpmname);
761 my ($requires, $envr) = $rpm->requires;
763 if (! defined $requires || ! defined $envr) {
764 $lock->log("Could not get run-time requires of RPM `" . $rpmname .
766 return $lock->mark_failed;
768 $$allrequires{$envr} = $requires;
771 if (! $lock->nstorereference($allrequires, $self->runrequiresstore)) {
772 $lock->log("Could not store run-time requires of RPM `". $self->name .
773 "' into `" . $self->runrequiresstore . "' file: $@\n");
774 return $lock->mark_failed;
777 $lock->log(Data
::Dumper
::Dumper
($allrequires));
778 return $lock->mark_done;
781 # Distill Provides from rebuilt binary packages and serialize them into file.
782 # Return true on success.
784 sub storebinaryprovides
{
786 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
787 state => 'providesstore');
788 if ($lock->is_done) { return 1; }
790 my @rpms = glob(File
::Spec
->catfile($self->rpmdir, '*.rpm'));
792 $lock->log("No binary RPM packages found in `" . $self->rpmdir
794 return $lock->mark_failed;
797 my $allprovides = {};
798 for my $rpmname (@rpms) {
799 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $rpmname);
801 my ($provides, $envr) = $rpm->provides;
803 if (! defined $provides || !defined $envr) {
804 $lock->log("Could not get provides of RPM `" . $rpmname . "': " .
806 return $lock->mark_failed;
808 $$allprovides{$envr} = $provides;
811 if (! $lock->nstorereference($allprovides, $self->providesstore)) {
812 $lock->log("Could not store provides of RPM `". $self->name .
813 "' into `" . $self->providesstore . "' file: $@\n");
814 return $lock->mark_failed;
817 $lock->log(Data
::Dumper
::Dumper
($allprovides));
818 return $lock->mark_done;
821 # Load run-time requires of already rebuilt binary packages from file.
822 # Return true on success.
823 # Needs `storebinaryrequires'.
824 sub loadbinaryrequires
{
826 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
827 state => 'runrequires');
828 if ($lock->is_done) { return 1; }
830 my $runrequires = $lock->retrievereference($self->runrequiresstore);
831 if (! $runrequires) {
832 $lock->log("Could not load run-time requires of `". $self->name .
833 "' package from `" . $self->runrequiresstore . "' file: $@\n");
834 return $lock->mark_failed;
836 $self->runrequires(shared_clone
($runrequires));
838 $lock->log(Data
::Dumper
::Dumper
($self->runrequires));
839 return $lock->mark_done;
842 # Load provides of already rebuilt binary packages from file.
843 # Return true on success.
844 # Needs `storebinaryprovides'.
845 sub loadbinaryprovides
{
847 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
848 state => 'provides');
849 if ($lock->is_done) { return 1; }
851 my $provides = $lock->retrievereference($self->providesstore);
853 $lock->log("Could not load provides of `". $self->name .
854 "' package from `" . $self->providesstore . "' file: $@\n");
855 return $lock->mark_failed;
857 $self->provides(shared_clone
($provides));
859 $lock->log(Data
::Dumper
::Dumper
($self->provides));
860 return $lock->mark_done;
863 # Increase package revision if not yet done. Commit change if first argument
865 # Return true on success.
868 my ($self, $mode) = @_;
869 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
871 if ($lock->is_done) { return 1; }
874 if (!$self->updatespec($lock)) {
875 $lock->log("Could not update spec file in order to create new " .
876 "update for `" . $self->name . "' package.\n");
877 return $lock->mark_failed;
881 if (!$lock->do($self->repodir, 'git', 'commit', '-a',
882 '-m', $self->message)) {
883 $lock->log("Could not commit changes into git repository `" .
884 $self->repodir . "'.\n");
885 return $lock->mark_failed;
888 if ($mode eq 'koji') {
890 if (!$lock->do($self->repodir, 'git', 'push')) {
891 $lock->log("Could not push changes from repository `" .
892 $self->repodir . "' to server.\n");
893 return $lock->mark_failed;
896 $lock->log("Not pushing changes because of local build mode.\n");
899 return $lock->mark_done;
903 # Submit package for building into Koji and store task ID.
904 # This is pointless in local build mode.
905 # First argument is Fedora::Rebuild::Package::Config object.
906 # Requires `clone'. Sould be called after `update'.
907 # Return true on success.
909 my ($self, $build_config) = @_;
910 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
911 state => 'submitbuild');
912 if ($lock->is_done) { return 1; }
914 # Get NEVRA of intended build
915 my $nevra = $self->get_nevra_from_git($lock, $build_config);
916 if (! defined $nevra) {
917 return $lock->mark_failed;
920 # Check the build is not already in Koji (e.g. by concurrent packager
921 # or after this program restart) but do not conclude anything.
923 if (!$lock->dooutput($self->repodir, \
$buildinfo, $build_config->koji(),
924 'buildinfo', $nevra)) {
925 $lock->log("Could not ask Koji for `" . $nevra . "' status " .
926 "before submitting new build.\n");
927 return $lock->mark_failed;
929 if ($buildinfo =~ /No such build/m) {
930 $lock->log("Package not yet submitted for building as expected.\n");
932 # Get task ID of already building package
933 if ($buildinfo =~ /Task:\s*(\d+)/m) {
934 # TODO: We could compare task target and consider as submitted if
935 # equaled to intended target.
937 $lock->log("Package `$nevra' already submitted as task " .
938 "`$task_id'. Previous build failed or somebody builds the " .
939 "package concurrently.\n");
941 $lock->log("Package `$nevra' already in Koji, but task ID " .
942 "could not been determined.\n");
944 $lock->log("Re-submitting the package.\n")
949 if (!$lock->dooutput($self->repodir, \
$task_id, $build_config->pyrpkg(),
950 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
951 'build', '--nowait', '--target', $self->target)) {
952 $lock->log("Could not submit `" . $nevra . "' into Koji.\n");
953 return $lock->mark_failed;
955 if (not $task_id =~ /Created task:\s*(\d+)/) {
956 $lock->log("Could not parse Koji task ID for `$nevra' build\n");
957 return $lock->mark_failed;
962 if (! $lock->nstorereference(\
$task_id, $self->taskstore)) {
963 $lock->log("Could not store task ID `" . $task_id . "' of `" . $nevra .
964 "' package into `" . $self->taskstore . "' file: $@\n");
965 return $lock->mark_failed;
967 $lock->log("Task ID `" . $task_id . "' stored into `" .
968 $self->taskstore . "' file sucessfully.\n");
971 return $lock->mark_done;
975 # First argument is Fedora::Rebuild::Package::Config module. If mode member is:
976 # "koji" wait for package build in Koji,
977 # "local" build locally using pyrpkg.
978 # "mock" build locally using mock.
979 # Second argument is Mock object to use instead of fresh one (value undef).
980 # (This is safe only in single-threaded run. This is limitation of mock.)
981 # Requires `clone'. Sould be called after `update' or `submitbuild'.
982 # Third argument is Mock object to use instead of fresh one (value undef) for
983 # building missing SRPMs.
984 # Return true on success.
986 my ($self, $build_config, $mock, $src_mock) = @_;
987 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
989 if ($lock->is_done) { return 1; }
991 my $nevra = $self->get_nevra_from_git($lock, $build_config);
992 if (! defined $nevra) {
993 return $lock->mark_failed;
996 if ($build_config->mode() eq 'local') {
997 if (!$lock->do($self->repodir, $build_config->pyrpkg(),
998 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
1000 $lock->log("Could not build `" . $nevra . "' locally.\n");
1001 return $lock->mark_failed;
1003 } elsif ($build_config->mode() eq 'koji') {
1004 # Retrieve task ID of submitted build
1005 my $task_id = $lock->retrievereference($self->taskstore);
1007 $lock->log("Could not load task ID for `". $nevra .
1008 "' build from `" . $self->taskstore . "' file: $@\n");
1009 return $lock->mark_failed;
1011 $task_id = $$task_id;
1013 # Wait for build task result
1014 # TODO: How to recognize the process died for other reason
1015 # than build failure?
1016 if (!$lock->do($self->repodir, $build_config->koji(),
1017 'watch-task', $task_id)) {
1018 $lock->log("Could not get status of Koji task `" . $task_id .
1019 "' for `$nevra' build, or the task failed.\n");
1020 return $lock->mark_failed;
1022 } elsif ($build_config->mode() eq 'mock') {
1023 my $srpm_name = $self->get_srpm_name($lock, $build_config, $src_mock);
1024 if (! defined $srpm_name) {
1025 return $lock->mark_failed;
1027 if (!replace_directory
($lock, $self->mockdir)) {
1028 return $lock->mark_failed;
1030 my ($mock_config_dir, $mock_config_root);
1032 if (!defined $mock) {
1033 $mock = Fedora
::Rebuild
::Mock
->new(
1034 architecture
=> $build_config->architecture(),
1035 repositories
=> $build_config->repositories(),
1036 install_packages
=> $build_config->mock_install_packages(),
1038 $mock->make_configuration;
1040 $mock_config_dir = $mock->config_dir;
1041 $mock_config_root = $mock->config_root;
1044 $lock->log("Could not configure mock: $@\n");
1045 File
::Path
::Tiny
::rm
($mock_config_dir);
1046 return $lock->mark_failed;
1048 my $success = $lock->do(undef, 'mock', '--resultdir', $self->mockdir,
1049 '--configdir', $mock_config_dir, '--root', $mock_config_root,
1050 '--rebuild', $srpm_name);
1051 if (defined $mock && !$mock->shared) {
1052 File
::Path
::Tiny
::rm
($mock_config_dir);
1055 $lock->log("Could not build `" . $nevra . "' in mock.\n");
1056 return $lock->mark_failed;
1059 $lock->log("Could not build `" . $nevra .
1060 "' because of unknown building mode `" . $build_config->mode() .
1062 return $lock->mark_failed;
1065 return $lock->mark_done;
1069 # Waits for build root rotation. Just built package will be available for
1070 # other packages at build time after returning from this fuction.
1071 # First argument is Fedora::Rebuild::Package::Config object.
1072 # If the mode is "koji", it will wait for the Koji rotation.
1073 # If the mode is "mock", it will create yum repository in directory with
1075 # If the mode is "local", this function is void.
1076 # Requires `update'. Sould be called after `build'.
1077 # Return true on success.
1078 sub dowaitforbuildroot
{
1079 my ($self, $build_config) = @_;
1080 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
1082 if ($lock->is_done) { return 1; }
1084 my $nevra = $self->get_nevra_from_git($lock, $build_config);
1085 if (! defined $nevra) {
1086 return $lock->mark_failed;
1089 if ($build_config->mode() eq 'koji') {
1090 if (!$lock->do($self->repodir, $build_config->koji(), 'wait-repo',
1091 '--build=' . $nevra, '--target', $self->target)) {
1092 $lock->log("Koji does not contain `" . $nevra .
1093 "' package in build root for `" . $self->target .
1094 "' build target yet.\n");
1095 return $lock->mark_failed;
1097 } elsif ($build_config->mode() eq 'mock') {
1098 $lock->log("`" . $nevra .
1099 "' built in mock, creating yum repository...\n");
1100 if (!$lock->do($self->rpmdir, 'createrepo_c', '.')) {
1101 $lock->log("Could not create yum repository for `" . $nevra .
1103 return $lock->mark_failed;
1105 $lock->log("`" . $nevra .
1106 "' yum repository created successfully.\n");
1108 $lock->log("`" . $nevra .
1109 "' built locally, not waiting on Koji rotation.\n");
1112 return $lock->mark_done;
1116 # Reset 'rotate' state by removing directory with binary packages produced by
1118 # TODO: Untag package from koji?
1119 # Return true on success, otherwise false.
1123 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(
1124 package => $self, state => 'rotate');
1125 $lock->log("Reseting state...\n");
1126 if (!remove_directory
($lock, $self->rpmdir)) {
1127 return $lock->mark_failed;
1135 # Set hash of build-time dependencies (requires attribute).
1136 # First argument is Fedora::Rebuild::Package::Config object. If mode member is:
1137 # "local" build SRPM locally using pyrpkg,
1138 # "koji" or "mock" build SRPM using mock.
1139 # Second argument is anonymous clone boolean.
1140 # Third argument is Mock object to use instead of fresh one (value undef).
1141 # (This is safe only in single-threaded run. This is limitation of mock.)
1142 # Fourth argument is Mock object to use instead of fresh one (value undef).
1143 # Return undef in case of failure.
1144 sub get_buildrequires
{
1145 my ($self, $build_config, $anonymous, $mock, $src_mock) = @_;
1147 print "Getting BuildRequires for `" . $self->name . "'...\n";
1149 # Procede all steps, each must be re-doable
1150 $ok = $self->clone($anonymous, $build_config);
1151 $ok = $self->srpm(0, $build_config, $mock) if $ok;
1152 $ok = $self->storebuildrequires($build_config, $src_mock) if $ok;
1153 $ok = $self->buildrequires if $ok;
1156 print "BuildRequires for `" . $self->name .
1157 "' package distilled successfully.\n";
1160 print "Could not get BuildRequires for `" . $self->name .
1166 # Set hash of run-time requires (Requires attribute).
1167 # Return true on success, undef in case of failure.
1168 # XXX: Requires `runrequiresstore'
1169 sub get_binaryrequires
{
1172 print "Getting run-time requires for `" . $self->name . "'...\n";
1174 $ok = $self->storebinaryrequires;
1175 $ok = $self->loadbinaryrequires if $ok;
1178 print "Run-time requires for `" . $self->name .
1179 "' package distilled successfully.\n";
1182 print "Could not get run-time requires for `" . $self->name .
1188 # Set hash of run-time provides (provides attribute).
1189 # Return true on success, undef in case of failure.
1190 # XXX: Requires `providesstore'
1191 sub get_binaryprovides
{
1194 print "Getting binary provides for `" . $self->name . "'...\n";
1196 $ok = $self->storebinaryprovides;
1197 $ok = $self->loadbinaryprovides if $ok;
1200 print "Provides for `" . $self->name .
1201 "' package distilled successfully.\n";
1204 print "Could not get Provides for `" . $self->name . "' package.\n";
1209 # Set hash of run-time requires (Requires attribute) and provides
1210 # (provides attribute). Common wrapper for getbinaryrequires() and
1211 # getbinaryprovides().
1212 # Return true on success, undef in case of failure.
1213 # XXX: Requires `runrequiresstore' and `providesstore'
1214 sub get_binarydependencies
{
1217 print "Getting binary dependencies for `" . $self->name . "'...\n";
1219 $ok = $self->get_binaryrequires;
1220 $ok = $self->get_binaryprovides if $ok;
1223 print "Binary dependencies for `" . $self->name .
1224 "' package distilled successfully.\n";
1227 print "Could not get binary dependencies for `" . $self->name .
1233 # Rebuild a package without waiting for propagation to next build root.
1234 # First argument is Fedora::Rebuild::Package::Config object. mode member
1235 # defines build mode:
1236 # "koji" publish commit and build in Koji,
1237 # "local" build locally without pushing commits to server,
1238 # "mock" build in mock without pushing commits to server.
1239 # Second argument is anonymous clone boolean.
1240 # Third argument is Mock object to use instead of fresh one (value undef).
1241 # (This is safe only in single-threaded run. This is limitation of mock.)
1243 my ($self, $build_config, $anonymous, $mock) = @_;
1245 print "Rebuilding `" . $self->name . "'...\n";
1247 # Proceed all steps, each must be re-doable
1248 $ok = $self->clone($anonymous, $build_config);
1249 $ok = $self->update($build_config->mode()) if $ok;
1250 $ok = $self->submitbuild($build_config)
1251 if ($ok and $build_config->mode() eq 'koji');
1252 $ok = $self->build($build_config, $mock) if $ok;
1253 $ok = $self->binaryrpm($build_config) if $ok;
1254 $ok = $self->get_binaryprovides if $ok;
1255 $ok = $self->get_binaryrequires if $ok;
1258 print "`" . $self->name . "' rebuild finished successfully.\n";
1260 print "`" . $self->name . "' rebuild failed.\n";
1265 # Wait for the package propagated into new build root.
1266 # First argument is Fedora::Rebuild::Package::Config object.
1267 # If the mode is "koji", waits for Koji build root rotation,
1268 # Otherwise waits locally (no-op).
1269 # XXX: Requires `update', should be called after rebuilding package.
1270 sub waitforbuildroot
{
1271 my ($self, $build_config) = @_;
1273 print "Waiting for `" . $self->name . "' to get to build root...\n";
1275 $ok = $self->dowaitforbuildroot($build_config);
1278 print "`" . $self->name . "' propagated successfully.\n";
1280 print "`" . $self->name . "' propagation failed.\n";