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.9.0");
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
::make_path
($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 # Return true on success.
160 my ($self, $anonymous) = @_;
161 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
163 if ($lock->is_done) { return 1; }
165 # XXX: fedpkg creates subdirectory named like package. The package name
166 # could clash with our file structure. Thus we need to clone into secure
167 # directory and move repository content to fixed name $self->repodir after
170 if (-d
$self->repodir) { File
::Path
::remove_tree
($self->repodir); }
171 my $tempdir = File
::Spec
->catfile($self->packagedir, 'tmp');
172 my $temprepodir = File
::Spec
->catfile($tempdir, $self->name);
173 if (-d
$tempdir) { File
::Path
::remove_tree
($tempdir); }
174 if (!File
::Path
::make_path
($tempdir)) {
175 $lock->log("Could not create directory `" . $tempdir . "': $!\n");
176 return $lock->mark_failed;
179 if (!$lock->do($tempdir, 'fedpkg', 'clone', ($anonymous) ?
'-a': (),
181 $lock->log("Could not clone `" . $self->name . "' repository.\n");
182 return $lock->mark_failed;
185 if (!rename($temprepodir, $self->repodir)) {
186 $lock->log("Could not move `" . $temprepodir . "' content to to `" .
187 $self->repodir . "'.\n");
188 return $lock->mark_failed;
190 File
::Path
::remove_tree
($tempdir);
192 if (!$lock->do($self->repodir, 'fedpkg', 'switch-branch', $self->branch)) {
193 $lock->log("Could not switch `" . $self->name .
194 "' repository to branch `" . $self->branch . "'.\n");
195 return $lock->mark_failed;
198 return $lock->mark_done;
202 # Increase package revision with proper changelog entry. Do not commit nor
204 # First argument is a state lock.
205 # XXX: The state is not marked as failed in case of error,
206 # Return true on success.
209 my ($self, $lock) = @_;
210 if (!defined $lock) {
214 # Reset git repository
215 if (!$lock->do($self->repodir, 'git', 'reset', '--hard',
216 'origin/' . $self->branch)) {
217 $lock->log("Could not reset git repository in `" . $self->repodir .
222 # Pull git repository
223 if (!$lock->do($self->repodir, 'git', 'pull')) {
224 $lock->log("Could not pull git repository in `" . $self->repodir .
229 # Increase revision number
230 my $specfile = File
::Spec
->catfile($self->repodir, $self->name . '.spec');
231 if (!$lock->do(undef, 'rpmdev-bumpspec', '-c', $self->message, $specfile)) {
232 $lock->log("Could not increase revison number in `" . $specfile .
241 # Builds SRPM. Return true on success.
242 # If first argument is true, recreate SRPM forcefully.
243 # If second argument is:
244 # "local" build SRPM locally using fedpkg,
245 # "koji" or "mock" build SRPM using mock.
246 # Third argument is reference to array of repository URLs to use as package
248 # Fourth argument is architecture.
249 # Fifth argument is Mock object to use instead of fresh one (value undef).
250 # (This is safe only in single-threaded run. This is limitation of mock.)
253 my ($self, $force, $mode, $repositories, $architecture, $mock) = @_;
254 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
256 if ($force) { $lock->remove_lock; }
257 if ($lock->is_done) { return 1; }
259 # We need do the update spec now to get the same NEVRA as in rebuild. We do
260 # update without pushing to prevent from repetitive NEVRA bumps in case of
262 if (!$self->updatespec($lock)) {
263 $lock->log("Could not update spec file in order to create new SRPM " .
264 "for `" . $self->name . "' package.\n");
265 return $lock->mark_failed;
268 if ($mode eq 'local') {
269 if (!$lock->do($self->repodir, 'fedpkg',
270 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
272 $lock->log("Could not build SRPM for `" . $self->name .
273 "' package locally.\n");
274 return $lock->mark_failed;
276 } elsif ($mode eq 'koji' or $mode eq 'mock') {
277 # Prepare spec file and sources
278 my $specname = File
::Spec
->catfile($self->repodir,
279 $self->name . '.spec');
280 if (!$lock->do($self->repodir, 'fedpkg',
281 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
283 $lock->log("Could not download sources for `" . $self->name .
285 return $lock->mark_failed;
289 if (!replace_directory
($lock, $self->mockdir)) {
290 return $lock->mark_failed;
292 my ($mock_config_dir, $mock_config_root);
294 if (!defined $mock) {
295 $mock = Fedora
::Rebuild
::Mock
->new(
296 architecture
=> $architecture,
297 repositories
=> $repositories,
299 $mock->make_configuration;
301 $mock_config_dir = $mock->config_dir;
302 $mock_config_root = $mock->config_root;
305 $lock->log("Could not configure mock: $@\n");
306 File
::Path
::remove_tree
($mock_config_dir);
307 return $lock->mark_failed;
309 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
310 '--configdir', $mock_config_dir, '--root', $mock_config_root,
311 ($mock->shared) ?
('--no-clean', '--no-cleanup-after') : (),
313 '--spec', $specname, '--sources', $self->repodir)) {
314 $lock->log("Could not build SRPM for `" . $self->name .
316 if (defined $mock && !$mock->shared) {
317 File
::Path
::remove_tree
($mock_config_dir);
319 return $lock->mark_failed;
321 if (defined $mock && !$mock->shared) {
322 File
::Path
::remove_tree
($mock_config_dir);
325 # Move SRPM from mockdir to repodir
326 my @srpms = glob(File
::Spec
->catfile($self->mockdir, '*.src.rpm'));
328 $lock->log("No SRPM package found under `" .
329 $self->mockdir . "'\n");
330 return $lock->mark_failed;
333 $lock->log("More SRPM packages found under `" .
334 $self->mockdir . "'. This should not happen.\n");
335 return $lock->mark_failed;
337 if (!copy_files_into_directory
($lock, $self->repodir, @srpms)) {
338 return $lock->mark_failed;
341 $lock->log("Could not build SRPM for `" . $self->name .
342 "' because of unknown building mode `" . $mode . "'.\n");
343 return $lock->mark_failed;
346 return $lock->mark_done;
349 # Get current package NEVRA from sources in repository.
350 # First argument is state lock where process of getting NEVRA including
351 # potential failure is logged.
352 # XXX: The state is not marked as failed in case of error,
353 # Return NEVRA string or undef in case of error.
354 sub get_nevra_from_git
{
355 my ($self, $lock) = @_;
358 if (!$lock->dooutput($self->repodir, \
$nevra, 'fedpkg',
359 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
362 $lock->log("Could not get NEVRA from `" . $self->name .
363 "' git repository package.\n");
367 # Consider last line only becuase of bug in fedpkg
368 # <https://bugzilla.redhat.com/show_bug.cgi?id=721389>.
369 my @lines = (split qr{$/}, $nevra);
375 # Get current package SRPM name.
376 # If the SRPM file does not exist, it will be re-created.
377 # First argument is state lock where process of building SRPM including
378 # potential failure is logged.
379 # If second argument is:
380 # "local" build SRPM locally using fedpkg,
381 # "koji" or "mock" build SRPM using mock.
382 # Third argument is reference to array of repository URLs to use as package
384 # Fourth argument is architecture.
385 # XXX: The state is not marked as failed in case of error,
386 # Return SRPM file name string or undef in case of error.
388 my ($self, $lock, $mode, $repositories, $architecture) = @_;
390 my $nevra = $self->get_nevra_from_git($lock);
391 if (! defined $nevra) {
395 my $srpmname = File
::Spec
->catfile($self->repodir, $nevra . '.src.rpm');
396 if (! -f
$srpmname ) {
397 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
398 "trying to create SRPM again...\n");
399 if (!$self->srpm(1, $mode, $repositories, $architecture)
401 $lock->log("`Could not recreate SRPM package '" . $srpmname .
409 # Create a directory. If it exists, it will remove it before.
410 # First argument is the directory, second argument is lock to log errors into.
411 # Return true, false in case of error.
412 # XXX: This is not a method
413 sub replace_directory
{
414 my ($lock, $directory) = @_;
415 if (-d
$directory) { File
::Path
::remove_tree
($directory); }
416 if (!File
::Path
::make_path
($directory)) {
417 $lock->log("Could not create directory `" . $directory . "': $!\n");
423 # Copy files into existing directory.
424 # First argument lock for logggin,
425 # second argument is destinatinon directory,
426 # The last is list of files to be copied.
427 # Return true in sucesss, false in case of error.
428 # XXX: This is not a method
429 sub copy_files_into_directory
{
430 my ($lock, $directory, @files) = @_;
431 for my $file (@files) {
433 if (!copy
($file, $directory)) {
434 $lock->log("Could not copy `". $file . "' into `". $directory .
438 $lock->log("`" . $file . "' copied into `" . $directory . "'\n");
443 # Destile BuildRequires from local SRPM and serialize them into file.
444 # If first argument is:
445 # "local" build SRPM locally using fedpkg,
446 # "koji" or "mock" build SRPM using mock.
447 # Second argument is reference to array of repository URLs to use as package
449 # Third argument is architecture.
450 # Return true on success.
452 # FIXME: does not work after cleaning clone or doing update.
453 sub storebuildrequires
{
454 my ($self, $mode, $repositories, $architecture) = @_;
455 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
456 state => 'buildrequiresstore');
457 if ($lock->is_done) { return 1; }
459 my $nevra = $self->get_nevra_from_git($lock);
460 if (! defined $nevra) {
461 return $lock->mark_failed;
464 my $srpmname = File
::Spec
->catfile($self->repodir, $nevra . '.src.rpm');
465 if (! -f
$srpmname ) {
466 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
467 "trying to create SRPM again...\n");
468 if (!$self->srpm(1, $mode, $repositories, $architecture)
470 $lock->log("`Could not recreate SRPM package '" . $srpmname .
472 return $lock->mark_failed;
476 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $srpmname);
477 my ($requires, $envra) = $rpm->requires;
478 if (! defined $requires) {
479 $lock->log("Could not get requires of SRPM `" . $srpmname . "': ". $@
481 return $lock->mark_failed;
484 if (! $lock->nstorereference($requires, $self->requiresstore)) {
485 $lock->log("Could not store requires of SRPM `". $srpmname .
486 "' into `" . $self->requiresstore . "' file: $@\n");
487 return $lock->mark_failed;
490 $lock->log(Data
::Dumper
::Dumper
($requires) . "\n");
491 return $lock->mark_done;
494 # Destile BuildRequires from local SRPM. Return true on success.
495 # Needs `buildrequiresstore'.
498 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
499 state => 'buildrequires');
500 if ($lock->is_done) { return 1; }
502 my $requires = $lock->retrievereference($self->requiresstore);
504 $lock->log("Could not load requires of `". $self->name .
505 "' package from `" . $self->requiresstore . "' file: $@\n");
506 return $lock->mark_failed;
508 $self->requires(shared_clone
($requires));
510 $lock->log(Data
::Dumper
::Dumper
($self->requires) . "\n");
511 return $lock->mark_done;
514 # Record verdict from dependency solver whether the package is rebuildable.
515 # This step is always redone.
516 sub log_is_rebuildable
{
517 my ($self, $is_rebuildable, $message) = @_;
518 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
519 state => 'is_rebuildable');
522 $lock->log("Solver result for possibility of rebuilding SRPM for `" .
523 $self->name . "': $message\n");
524 if (! $is_rebuildable) {
525 $lock->log("According dependency solver, this package is not " .
526 "rebuildable now.\n");
527 return $lock->mark_failed;
530 $lock->log("According dependency solver, this package is " .
531 "rebuildable now.\n");
532 return $lock->mark_done;
535 # Get binary RPM packages for the source package.
536 # If first argument is:
537 # 'koji' download them from Koji,
538 # 'local' from local build,
539 # 'mock' from mock result directory.
540 # Second argument is RPM architecture. noarch packages are colletected
542 # Requires `clone'. Sould be called after `build'.
543 # Return true on success.
545 my ($self, $mode, $architecture) = @_;
546 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
548 if ($lock->is_done) { return 1; }
550 if (!replace_directory
($lock, $self->rpmdir)) {
551 return $lock->mark_failed;
554 my @archs = ($architecture, 'noarch');
555 if ($mode eq 'koji') {
556 $lock->log("Getting binary RPM packages from Koji:\n");
558 my $nevra = $self->get_nevra_from_git($lock);
559 if (! defined $nevra) {
560 return $lock->mark_failed;
563 # TODO: Get all archs, remove SRPM
564 if (!$lock->do($self->rpmdir, 'koji', 'download-build',
565 (map {'--arch=' . $_ } @archs), $nevra)) {
566 $lock->log("Could not get binary RPM packages for `" . $nevra .
568 return $lock->mark_failed;
570 } elsif ($mode eq 'local') {
571 $lock->log("Getting binary RPM packages from local build:\n");
573 my @rpms = map { glob(File
::Spec
->catfile($self->repodir, $_,
576 $lock->log("No binary RPM packages found under `" .
577 $self->repodir . "'\n");
578 return $lock->mark_failed;
581 if (!copy_files_into_directory
($lock, $self->rpmdir, @rpms)) {
582 return $lock->mark_failed;
584 } elsif ($mode eq 'mock') {
585 $lock->log("Getting binary RPM packages from mock build:\n");
587 my @rpms = map { glob(File
::Spec
->catfile($self->mockdir,
588 ('*.' . $_ . '.rpm'))) } @archs;
590 $lock->log("No binary RPM packages found under `" .
591 $self->mockdir . "'\n");
592 return $lock->mark_failed;
595 if (!copy_files_into_directory
($lock, $self->rpmdir, @rpms)) {
596 return $lock->mark_failed;
599 $lock->log("Could get binary RPM packages for `" . $self->name .
600 "' source package because of unknown building mode `" . $mode .
602 return $lock->mark_failed;
605 return $lock->mark_done;
608 # Return list of binary RPM files relative to current working directory.
609 # XXX: Should be called after colleting the files from build process by
611 sub listbinaryrpmfiles
{
613 return (glob(File
::Spec
->catfile($self->rpmdir, '*.rpm')));
616 # Distill Requires from rebuilt binary packages and serialize them into file.
617 # Return true on success.
619 sub storebinaryrequires
{
621 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
622 state => 'runrequiresstore');
623 if ($lock->is_done) { return 1; }
625 my @rpms = glob(File
::Spec
->catfile($self->rpmdir, '*.rpm'));
627 $lock->log("No binary RPM packages found in `" . $self->rpmdir
629 return $lock->mark_failed;
632 my $allrequires = {};
633 for my $rpmname (@rpms) {
634 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $rpmname);
636 my ($requires, $envr) = $rpm->requires;
638 if (! defined $requires || ! defined $envr) {
639 $lock->log("Could not get run-time requires of RPM `" . $rpmname .
641 return $lock->mark_failed;
643 $$allrequires{$envr} = $requires;
646 if (! $lock->nstorereference($allrequires, $self->runrequiresstore)) {
647 $lock->log("Could not store run-time requires of RPM `". $self->name .
648 "' into `" . $self->runrequiresstore . "' file: $@\n");
649 return $lock->mark_failed;
652 $lock->log(Data
::Dumper
::Dumper
($allrequires));
653 return $lock->mark_done;
656 # Distill Provides from rebuilt binary packages and serialize them into file.
657 # Return true on success.
659 sub storebinaryprovides
{
661 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
662 state => 'providesstore');
663 if ($lock->is_done) { return 1; }
665 my @rpms = glob(File
::Spec
->catfile($self->rpmdir, '*.rpm'));
667 $lock->log("No binary RPM packages found in `" . $self->rpmdir
669 return $lock->mark_failed;
672 my $allprovides = {};
673 for my $rpmname (@rpms) {
674 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $rpmname);
676 my ($provides, $envr) = $rpm->provides;
678 if (! defined $provides || !defined $envr) {
679 $lock->log("Could not get provides of RPM `" . $rpmname . "': " .
681 return $lock->mark_failed;
683 $$allprovides{$envr} = $provides;
686 if (! $lock->nstorereference($allprovides, $self->providesstore)) {
687 $lock->log("Could not store provides of RPM `". $self->name .
688 "' into `" . $self->providesstore . "' file: $@\n");
689 return $lock->mark_failed;
692 $lock->log(Data
::Dumper
::Dumper
($allprovides));
693 return $lock->mark_done;
696 # Load run-time requires of already rebuilt binary packages from file.
697 # Return true on success.
698 # Needs `storebinaryrequires'.
699 sub loadbinaryrequires
{
701 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
702 state => 'runrequires');
703 if ($lock->is_done) { return 1; }
705 my $runrequires = $lock->retrievereference($self->runrequiresstore);
706 if (! $runrequires) {
707 $lock->log("Could not load run-time requires of `". $self->name .
708 "' package from `" . $self->runrequiresstore . "' file: $@\n");
709 return $lock->mark_failed;
711 $self->runrequires(shared_clone
($runrequires));
713 $lock->log(Data
::Dumper
::Dumper
($self->runrequires));
714 return $lock->mark_done;
717 # Load provides of already rebuilt binary packages from file.
718 # Return true on success.
719 # Needs `storebinaryprovides'.
720 sub loadbinaryprovides
{
722 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
723 state => 'provides');
724 if ($lock->is_done) { return 1; }
726 my $provides = $lock->retrievereference($self->providesstore);
728 $lock->log("Could not load provides of `". $self->name .
729 "' package from `" . $self->providesstore . "' file: $@\n");
730 return $lock->mark_failed;
732 $self->provides(shared_clone
($provides));
734 $lock->log(Data
::Dumper
::Dumper
($self->provides));
735 return $lock->mark_done;
738 # Increase package revision if not yet done. Commit change if first argument
740 # Return true on success.
743 my ($self, $mode) = @_;
744 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
746 if ($lock->is_done) { return 1; }
749 if (!$self->updatespec($lock)) {
750 $lock->log("Could not update spec file in order to create new " .
751 "update for `" . $self->name . "' package.\n");
752 return $lock->mark_failed;
756 if (!$lock->do($self->repodir, 'git', 'commit', '-a',
757 '-m', $self->message)) {
758 $lock->log("Could not commit changes into git repository `" .
759 $self->repodir . "'.\n");
760 return $lock->mark_failed;
763 if ($mode eq 'koji') {
765 if (!$lock->do($self->repodir, 'git', 'push')) {
766 $lock->log("Could not push changes from repository `" .
767 $self->repodir . "' to server.\n");
768 return $lock->mark_failed;
771 $lock->log("Not pushing changes because of local build mode.\n");
774 return $lock->mark_done;
778 # Submit package for building into Koji and store task ID.
779 # This is pointless in local build mode.
780 # Requires `clone'. Sould be called after `update'.
781 # Return true on success.
784 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
785 state => 'submitbuild');
786 if ($lock->is_done) { return 1; }
788 # Get NEVRA of intended build
789 my $nevra = $self->get_nevra_from_git($lock);
790 if (! defined $nevra) {
791 return $lock->mark_failed;
794 # Check the build is not already in Koji (e.g. by concurrent packager
795 # or after this program restart) but do not conclude anything.
797 if (!$lock->dooutput($self->repodir, \
$buildinfo, 'koji', 'buildinfo',
799 $lock->log("Could not ask Koji for `" . $nevra . "' status " .
800 "before submitting new build.\n");
801 return $lock->mark_failed;
803 if ($buildinfo =~ /No such build/m) {
804 $lock->log("Package not yet submitted for building as expected.\n");
806 # Get task ID of already building package
807 if ($buildinfo =~ /Task:\s*(\d+)/m) {
808 # TODO: We could compare task target and consider as submitted if
809 # equaled to intended target.
811 $lock->log("Package `$nevra' already submitted as task " .
812 "`$task_id'. Previous build failed or somebody builds the " .
813 "package concurrently.\n");
815 $lock->log("Package `$nevra' already in Koji, but task ID " .
816 "could not been determined.\n");
818 $lock->log("Re-submitting the package.\n")
823 if (!$lock->dooutput($self->repodir, \
$task_id, 'fedpkg',
824 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
825 'build', '--nowait', '--target', $self->target)) {
826 $lock->log("Could not submit `" . $nevra . "' into Koji.\n");
827 return $lock->mark_failed;
829 if (not $task_id =~ /Created task:\s*(\d+)/) {
830 $lock->log("Could not parse Koji task ID for `$nevra' build\n");
831 return $lock->mark_failed;
836 if (! $lock->nstorereference(\
$task_id, $self->taskstore)) {
837 $lock->log("Could not store task ID `" . $task_id . "' of `" . $nevra .
838 "' package into `" . $self->taskstore . "' file: $@\n");
839 return $lock->mark_failed;
841 $lock->log("Task ID `" . $task_id . "' stored into `" .
842 $self->taskstore . "' file sucessfully.\n");
845 return $lock->mark_done;
849 # If first argument is:
850 # "koji" wait for package build in Koji,
851 # "local" build locally using fedpkg.
852 # "mock" build locally using mock.
853 # Second argument is reference to array of repository URLs to use as package
855 # Third argument is architecture.
856 # base when building the package.
857 # Requires `clone'. Sould be called after `update' or `submitbuild'.
858 # Return true on success.
860 my ($self, $mode, $repositories, $architecture) = @_;
861 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
863 if ($lock->is_done) { return 1; }
865 my $nevra = $self->get_nevra_from_git($lock);
866 if (! defined $nevra) {
867 return $lock->mark_failed;
870 if ($mode eq 'local') {
871 if (!$lock->do($self->repodir, 'fedpkg',
872 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
874 $lock->log("Could not build `" . $nevra . "' locally.\n");
875 return $lock->mark_failed;
877 } elsif ($mode eq 'koji') {
878 # Retrieve task ID of submitted build
879 my $task_id = $lock->retrievereference($self->taskstore);
881 $lock->log("Could not load task ID for `". $nevra .
882 "' build from `" . $self->taskstore . "' file: $@\n");
883 return $lock->mark_failed;
885 $task_id = $$task_id;
887 # Wait for build task result
888 # TODO: How to recognize the process died for other reason
889 # than build failure?
890 if (!$lock->do($self->repodir, 'koji', 'watch-task', $task_id)) {
891 $lock->log("Could not get status of Koji task `" . $task_id .
892 "' for `$nevra' build, or the task failed.\n");
893 return $lock->mark_failed;
895 } elsif ($mode eq 'mock') {
896 my $srpm_name = $self->get_srpm_name($lock, $mode, $repositories,
898 if (! defined $srpm_name) {
899 return $lock->mark_failed;
901 if (!replace_directory
($lock, $self->mockdir)) {
902 return $lock->mark_failed;
904 my ($mock_config_dir, $mock_config_root);
906 ($mock_config_dir, $mock_config_root) =
907 Fedora
::Rebuild
::Mock
->new(
908 architecture
=> $architecture,
909 repositories
=> $repositories
910 )->make_configuration;
913 $lock->log("Could not configure mock: $@\n");
914 File
::Path
::remove_tree
($mock_config_dir);
915 return $lock->mark_failed;
917 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
918 '--configdir', $mock_config_dir, '--root', $mock_config_root,
919 '--rebuild', $srpm_name)) {
920 $lock->log("Could not build `" . $nevra . "' in mock.\n");
921 File
::Path
::remove_tree
($mock_config_dir);
922 return $lock->mark_failed;
924 File
::Path
::remove_tree
($mock_config_dir);
926 $lock->log("Could not build `" . $nevra .
927 "' because of unknown building mode `" . $mode . "'.\n");
928 return $lock->mark_failed;
931 return $lock->mark_done;
935 # Waits for build root rotation. Just built package will be available for
936 # other packages at build time after returning from this fuction.
937 # If first argument is "koji", it will wait for the Koji rotation.
938 # If first argument is "mock", it will create yum repository in directory with
940 # If first argument is "local", this function is void,
941 # Requires `update'. Sould be called after `build'.
942 # Return true on success.
943 sub dowaitforbuildroot
{
944 my ($self, $mode) = @_;
945 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
947 if ($lock->is_done) { return 1; }
949 my $nevra = $self->get_nevra_from_git($lock);
950 if (! defined $nevra) {
951 return $lock->mark_failed;
954 if ($mode eq 'koji') {
955 if (!$lock->do($self->repodir, 'koji', 'wait-repo',
956 '--build=' . $nevra, '--target', $self->target)) {
957 $lock->log("Koji does not contain `" . $nevra .
958 "' package in build root for `" . $self->target .
959 "' build target yet.\n");
960 return $lock->mark_failed;
962 } elsif ($mode eq 'mock') {
963 $lock->log("`" . $nevra .
964 "' built in mock, creating yum repository...\n");
965 if (!$lock->do($self->rpmdir, 'createrepo', '.')) {
966 $lock->log("Could not create yum repository for `" . $nevra .
968 return $lock->mark_failed;
970 $lock->log("`" . $nevra .
971 "' yum repository created successfully.\n");
973 $lock->log("`" . $nevra .
974 "' built locally, not waiting on Koji rotation.\n");
977 return $lock->mark_done;
980 # Set hash of build-time dependencies (requires attribute).
981 # If first argument is:
982 # "local" build SRPM locally using fedpkg,
983 # "koji" or "mock" build SRPM using mock.
984 # Second argument is reference to array of repository URLs to use as package
986 # Third argument is architecture.
987 # Fourth argument is anonymous clone boolean.
988 # Fifth argument is Mock object to use instead of fresh one (value undef).
989 # (This is safe only in single-threaded run. This is limitation of mock.)
990 # Return undef in case of failure.
991 sub get_buildrequires
{
992 my ($self, $mode, $repositories, $architecture, $anonymous, $mock) = @_;
994 print "Getting BuildRequires for `" . $self->name . "'...\n";
996 # Procede all steps, each must be re-doable
997 $ok = $self->clone($anonymous);
998 $ok = $self->srpm(0, $mode, $repositories, $architecture, $mock) if $ok;
999 $ok = $self->storebuildrequires($mode, $repositories, $architecture) if $ok;
1000 $ok = $self->buildrequires if $ok;
1003 print "BuildRequires for `" . $self->name .
1004 "' package distilled successfully.\n";
1007 print "Could not get BuildRequires for `" . $self->name .
1013 # Set hash of run-time requires (Requires attribute).
1014 # Return true on success, undef in case of failure.
1015 # XXX: Requires `runrequiresstore'
1016 sub get_binaryrequires
{
1019 print "Getting run-time requires for `" . $self->name . "'...\n";
1021 $ok = $self->storebinaryrequires;
1022 $ok = $self->loadbinaryrequires if $ok;
1025 print "Run-time requires for `" . $self->name .
1026 "' package distilled successfully.\n";
1029 print "Could not get run-time requires for `" . $self->name .
1035 # Set hash of run-time provides (provides attribute).
1036 # Return true on success, undef in case of failure.
1037 # XXX: Requires `providesstore'
1038 sub get_binaryprovides
{
1041 print "Getting binary provides for `" . $self->name . "'...\n";
1043 $ok = $self->storebinaryprovides;
1044 $ok = $self->loadbinaryprovides if $ok;
1047 print "Provides for `" . $self->name .
1048 "' package distilled successfully.\n";
1051 print "Could not get Provides for `" . $self->name . "' package.\n";
1056 # Set hash of run-time requires (Requires attribute) and provides
1057 # (provides attribute). Common wrapper for getbinaryrequires() and
1058 # getbinaryprovides().
1059 # Return true on success, undef in case of failure.
1060 # XXX: Requires `runrequiresstore' and `providesstore'
1061 sub get_binarydependencies
{
1064 print "Getting binary dependencies for `" . $self->name . "'...\n";
1066 $ok = $self->get_binaryrequires;
1067 $ok = $self->get_binaryprovides if $ok;
1070 print "Binary dependencies for `" . $self->name .
1071 "' package distilled successfully.\n";
1074 print "Could not get binary dependencies for `" . $self->name .
1080 # Rebuild a package without waiting for propagation to next build root.
1081 # First argument defines build mode:
1082 # "koji" publish commit and build in Koji,
1083 # "local" build locally without pushing commits to server,
1084 # "mock" build in mock without pushing commits to server.
1085 # Second argument is reference to array of repository URLs to use as package
1086 # base when building the package.
1087 # Third argument is architecture.
1088 # Fourth argument is anonymous clone boolean.
1090 my ($self, $mode, $repositories, $architecture, $anonymous) = @_;
1092 print "Rebuilding `" . $self->name . "'...\n";
1094 # Proceed all steps, each must be re-doable
1095 $ok = $self->clone($anonymous);
1096 $ok = $self->update($mode) if $ok;
1097 $ok = $self->submitbuild if ($ok and $mode eq 'koji');
1098 $ok = $self->build($mode, $repositories, $architecture) if $ok;
1099 $ok = $self->binaryrpm($mode, $architecture) if $ok;
1100 $ok = $self->get_binaryprovides if $ok;
1101 $ok = $self->get_binaryrequires if $ok;
1104 print "`" . $self->name . "' rebuild finished successfully.\n";
1106 print "`" . $self->name . "' rebuild failed.\n";
1111 # Wait for the package propagated into new build root.
1112 # If first argument is "koji", waits for Koji build root rotation,
1113 # Otherwise waits locally (no-op).
1114 # XXX: Requires `update', should be called after rebuilding package.
1115 sub waitforbuildroot
{
1116 my ($self, $mode) = @_;
1118 print "Waiting for `" . $self->name . "' to get to build root...\n";
1120 $ok = $self->dowaitforbuildroot($mode);
1123 print "`" . $self->name . "' propagated successfully.\n";
1125 print "`" . $self->name . "' propagation failed.\n";