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.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
::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 (!Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
263 state => 'update')->is_done) {
264 if (!$self->updatespec($lock)) {
265 $lock->log("Could not update spec file in order to create new " .
266 "SRPM for `" . $self->name . "' package.\n");
267 return $lock->mark_failed;
271 if ($mode eq 'local') {
272 if (!$lock->do($self->repodir, 'fedpkg',
273 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
275 $lock->log("Could not build SRPM for `" . $self->name .
276 "' package locally.\n");
277 return $lock->mark_failed;
279 } elsif ($mode eq 'koji' or $mode eq 'mock') {
280 # Prepare spec file and sources
281 my $specname = File
::Spec
->catfile($self->repodir,
282 $self->name . '.spec');
283 if (!$lock->do($self->repodir, 'fedpkg',
284 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
286 $lock->log("Could not download sources for `" . $self->name .
288 return $lock->mark_failed;
292 if (!replace_directory
($lock, $self->mockdir)) {
293 return $lock->mark_failed;
295 my ($mock_config_dir, $mock_config_root);
297 if (!defined $mock) {
298 $mock = Fedora
::Rebuild
::Mock
->new(
299 architecture
=> $architecture,
300 repositories
=> $repositories,
302 $mock->make_configuration;
304 $mock_config_dir = $mock->config_dir;
305 $mock_config_root = $mock->config_root;
308 $lock->log("Could not configure mock: $@\n");
309 File
::Path
::remove_tree
($mock_config_dir);
310 return $lock->mark_failed;
312 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
313 '--configdir', $mock_config_dir, '--root', $mock_config_root,
314 ($mock->shared) ?
('--no-clean', '--no-cleanup-after') : (),
316 '--spec', $specname, '--sources', $self->repodir)) {
317 $lock->log("Could not build SRPM for `" . $self->name .
319 if (defined $mock && !$mock->shared) {
320 File
::Path
::remove_tree
($mock_config_dir);
322 return $lock->mark_failed;
324 if (defined $mock && !$mock->shared) {
325 File
::Path
::remove_tree
($mock_config_dir);
328 # Move SRPM from mockdir to repodir
329 my @srpms = glob(File
::Spec
->catfile($self->mockdir, '*.src.rpm'));
331 $lock->log("No SRPM package found under `" .
332 $self->mockdir . "'\n");
333 return $lock->mark_failed;
336 $lock->log("More SRPM packages found under `" .
337 $self->mockdir . "'. This should not happen.\n");
338 return $lock->mark_failed;
340 if (!copy_files_into_directory
($lock, $self->repodir, @srpms)) {
341 return $lock->mark_failed;
344 $lock->log("Could not build SRPM for `" . $self->name .
345 "' because of unknown building mode `" . $mode . "'.\n");
346 return $lock->mark_failed;
349 return $lock->mark_done;
352 # Get current package NEVRA from sources in repository.
353 # First argument is state lock where process of getting NEVRA including
354 # potential failure is logged.
355 # XXX: The state is not marked as failed in case of error,
356 # Return NEVRA string or undef in case of error.
357 sub get_nevra_from_git
{
358 my ($self, $lock) = @_;
361 if (!$lock->dooutput($self->repodir, \
$nevra, 'fedpkg',
362 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
365 $lock->log("Could not get NEVRA from `" . $self->name .
366 "' git repository package.\n");
370 # Consider last line only becuase of bug in fedpkg
371 # <https://bugzilla.redhat.com/show_bug.cgi?id=721389>.
372 my @lines = (split qr{$/}, $nevra);
378 # Get current package SRPM name.
379 # If the SRPM file does not exist, it will be re-created.
380 # First argument is state lock where process of building SRPM including
381 # potential failure is logged.
382 # If second argument is:
383 # "local" build SRPM locally using fedpkg,
384 # "koji" or "mock" build SRPM using mock.
385 # Third argument is reference to array of repository URLs to use as package
387 # Fourth argument is architecture.
388 # XXX: The state is not marked as failed in case of error,
389 # Return SRPM file name string or undef in case of error.
391 my ($self, $lock, $mode, $repositories, $architecture) = @_;
393 my $nevra = $self->get_nevra_from_git($lock);
394 if (! defined $nevra) {
398 my $srpmname = File
::Spec
->catfile($self->repodir, $nevra . '.src.rpm');
399 if (! -f
$srpmname ) {
400 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
401 "trying to create SRPM again...\n");
402 if (!$self->srpm(1, $mode, $repositories, $architecture)
404 $lock->log("`Could not recreate SRPM package '" . $srpmname .
412 # Create a directory. If it exists, it will remove it before.
413 # First argument is the directory, second argument is lock to log errors into.
414 # Return true, false in case of error.
415 # XXX: This is not a method
416 sub replace_directory
{
417 my ($lock, $directory) = @_;
418 if (-d
$directory) { File
::Path
::remove_tree
($directory); }
419 if (!File
::Path
::make_path
($directory)) {
420 $lock->log("Could not create directory `" . $directory . "': $!\n");
426 # Copy files into existing directory.
427 # First argument lock for logggin,
428 # second argument is destinatinon directory,
429 # The last is list of files to be copied.
430 # Return true in sucesss, false in case of error.
431 # XXX: This is not a method
432 sub copy_files_into_directory
{
433 my ($lock, $directory, @files) = @_;
434 for my $file (@files) {
436 if (!copy
($file, $directory)) {
437 $lock->log("Could not copy `". $file . "' into `". $directory .
441 $lock->log("`" . $file . "' copied into `" . $directory . "'\n");
446 # Destile BuildRequires from local SRPM and serialize them into file.
447 # If first argument is:
448 # "local" build SRPM locally using fedpkg,
449 # "koji" or "mock" build SRPM using mock.
450 # Second argument is reference to array of repository URLs to use as package
452 # Third argument is architecture.
453 # Return true on success.
455 # FIXME: does not work after cleaning clone or doing update.
456 sub storebuildrequires
{
457 my ($self, $mode, $repositories, $architecture) = @_;
458 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
459 state => 'buildrequiresstore');
460 if ($lock->is_done) { return 1; }
462 my $nevra = $self->get_nevra_from_git($lock);
463 if (! defined $nevra) {
464 return $lock->mark_failed;
467 my $srpmname = File
::Spec
->catfile($self->repodir, $nevra . '.src.rpm');
468 if (! -f
$srpmname ) {
469 $lock->log("SRPM package `" . $srpmname . "' is missing, " .
470 "trying to create SRPM again...\n");
471 if (!$self->srpm(1, $mode, $repositories, $architecture)
473 $lock->log("`Could not recreate SRPM package '" . $srpmname .
475 return $lock->mark_failed;
479 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $srpmname);
480 my ($requires, $envra) = $rpm->requires;
481 if (! defined $requires) {
482 $lock->log("Could not get requires of SRPM `" . $srpmname . "': ". $@
484 return $lock->mark_failed;
487 if (! $lock->nstorereference($requires, $self->requiresstore)) {
488 $lock->log("Could not store requires of SRPM `". $srpmname .
489 "' into `" . $self->requiresstore . "' file: $@\n");
490 return $lock->mark_failed;
493 $lock->log(Data
::Dumper
::Dumper
($requires) . "\n");
494 return $lock->mark_done;
497 # Destile BuildRequires from local SRPM. Return true on success.
498 # Needs `buildrequiresstore'.
501 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
502 state => 'buildrequires');
503 if ($lock->is_done) { return 1; }
505 my $requires = $lock->retrievereference($self->requiresstore);
507 $lock->log("Could not load requires of `". $self->name .
508 "' package from `" . $self->requiresstore . "' file: $@\n");
509 return $lock->mark_failed;
511 $self->requires(shared_clone
($requires));
513 $lock->log(Data
::Dumper
::Dumper
($self->requires) . "\n");
514 return $lock->mark_done;
517 # Record verdict from dependency solver whether the package is rebuildable.
518 # This step is always redone.
519 sub log_is_rebuildable
{
520 my ($self, $is_rebuildable, $message) = @_;
521 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
522 state => 'is_rebuildable');
525 $lock->log("Solver result for possibility of rebuilding SRPM for `" .
526 $self->name . "': $message\n");
527 if (! $is_rebuildable) {
528 $lock->log("According dependency solver, this package is not " .
529 "rebuildable now.\n");
530 return $lock->mark_failed;
533 $lock->log("According dependency solver, this package is " .
534 "rebuildable now.\n");
535 return $lock->mark_done;
538 # Get binary RPM packages for the source package.
539 # If first argument is:
540 # 'koji' download them from Koji,
541 # 'local' from local build,
542 # 'mock' from mock result directory.
543 # Second argument is RPM architecture. noarch packages are colletected
545 # Requires `clone'. Sould be called after `build'.
546 # Return true on success.
548 my ($self, $mode, $architecture) = @_;
549 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
551 if ($lock->is_done) { return 1; }
553 if (!replace_directory
($lock, $self->rpmdir)) {
554 return $lock->mark_failed;
557 my @archs = ($architecture, 'noarch');
558 if ($mode eq 'koji') {
559 $lock->log("Getting binary RPM packages from Koji:\n");
561 my $nevra = $self->get_nevra_from_git($lock);
562 if (! defined $nevra) {
563 return $lock->mark_failed;
566 # TODO: Get all archs, remove SRPM
567 if (!$lock->do($self->rpmdir, 'koji', 'download-build',
568 (map {'--arch=' . $_ } @archs), $nevra)) {
569 $lock->log("Could not get binary RPM packages for `" . $nevra .
571 return $lock->mark_failed;
573 } elsif ($mode eq 'local') {
574 $lock->log("Getting binary RPM packages from local build:\n");
576 my @rpms = map { glob(File
::Spec
->catfile($self->repodir, $_,
579 $lock->log("No binary RPM packages found under `" .
580 $self->repodir . "'\n");
581 return $lock->mark_failed;
584 if (!copy_files_into_directory
($lock, $self->rpmdir, @rpms)) {
585 return $lock->mark_failed;
587 } elsif ($mode eq 'mock') {
588 $lock->log("Getting binary RPM packages from mock build:\n");
590 my @rpms = map { glob(File
::Spec
->catfile($self->mockdir,
591 ('*.' . $_ . '.rpm'))) } @archs;
593 $lock->log("No binary RPM packages found under `" .
594 $self->mockdir . "'\n");
595 return $lock->mark_failed;
598 if (!copy_files_into_directory
($lock, $self->rpmdir, @rpms)) {
599 return $lock->mark_failed;
602 $lock->log("Could get binary RPM packages for `" . $self->name .
603 "' source package because of unknown building mode `" . $mode .
605 return $lock->mark_failed;
608 return $lock->mark_done;
611 # Return list of binary RPM files relative to current working directory.
612 # XXX: Should be called after colleting the files from build process by
614 sub listbinaryrpmfiles
{
616 return (glob(File
::Spec
->catfile($self->rpmdir, '*.rpm')));
619 # Distill Requires from rebuilt binary packages and serialize them into file.
620 # Return true on success.
622 sub storebinaryrequires
{
624 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
625 state => 'runrequiresstore');
626 if ($lock->is_done) { return 1; }
628 my @rpms = glob(File
::Spec
->catfile($self->rpmdir, '*.rpm'));
630 $lock->log("No binary RPM packages found in `" . $self->rpmdir
632 return $lock->mark_failed;
635 my $allrequires = {};
636 for my $rpmname (@rpms) {
637 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $rpmname);
639 my ($requires, $envr) = $rpm->requires;
641 if (! defined $requires || ! defined $envr) {
642 $lock->log("Could not get run-time requires of RPM `" . $rpmname .
644 return $lock->mark_failed;
646 $$allrequires{$envr} = $requires;
649 if (! $lock->nstorereference($allrequires, $self->runrequiresstore)) {
650 $lock->log("Could not store run-time requires of RPM `". $self->name .
651 "' into `" . $self->runrequiresstore . "' file: $@\n");
652 return $lock->mark_failed;
655 $lock->log(Data
::Dumper
::Dumper
($allrequires));
656 return $lock->mark_done;
659 # Distill Provides from rebuilt binary packages and serialize them into file.
660 # Return true on success.
662 sub storebinaryprovides
{
664 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
665 state => 'providesstore');
666 if ($lock->is_done) { return 1; }
668 my @rpms = glob(File
::Spec
->catfile($self->rpmdir, '*.rpm'));
670 $lock->log("No binary RPM packages found in `" . $self->rpmdir
672 return $lock->mark_failed;
675 my $allprovides = {};
676 for my $rpmname (@rpms) {
677 my $rpm = Fedora
::Rebuild
::RPM
->new(name
=> $rpmname);
679 my ($provides, $envr) = $rpm->provides;
681 if (! defined $provides || !defined $envr) {
682 $lock->log("Could not get provides of RPM `" . $rpmname . "': " .
684 return $lock->mark_failed;
686 $$allprovides{$envr} = $provides;
689 if (! $lock->nstorereference($allprovides, $self->providesstore)) {
690 $lock->log("Could not store provides of RPM `". $self->name .
691 "' into `" . $self->providesstore . "' file: $@\n");
692 return $lock->mark_failed;
695 $lock->log(Data
::Dumper
::Dumper
($allprovides));
696 return $lock->mark_done;
699 # Load run-time requires of already rebuilt binary packages from file.
700 # Return true on success.
701 # Needs `storebinaryrequires'.
702 sub loadbinaryrequires
{
704 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
705 state => 'runrequires');
706 if ($lock->is_done) { return 1; }
708 my $runrequires = $lock->retrievereference($self->runrequiresstore);
709 if (! $runrequires) {
710 $lock->log("Could not load run-time requires of `". $self->name .
711 "' package from `" . $self->runrequiresstore . "' file: $@\n");
712 return $lock->mark_failed;
714 $self->runrequires(shared_clone
($runrequires));
716 $lock->log(Data
::Dumper
::Dumper
($self->runrequires));
717 return $lock->mark_done;
720 # Load provides of already rebuilt binary packages from file.
721 # Return true on success.
722 # Needs `storebinaryprovides'.
723 sub loadbinaryprovides
{
725 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
726 state => 'provides');
727 if ($lock->is_done) { return 1; }
729 my $provides = $lock->retrievereference($self->providesstore);
731 $lock->log("Could not load provides of `". $self->name .
732 "' package from `" . $self->providesstore . "' file: $@\n");
733 return $lock->mark_failed;
735 $self->provides(shared_clone
($provides));
737 $lock->log(Data
::Dumper
::Dumper
($self->provides));
738 return $lock->mark_done;
741 # Increase package revision if not yet done. Commit change if first argument
743 # Return true on success.
746 my ($self, $mode) = @_;
747 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
749 if ($lock->is_done) { return 1; }
752 if (!$self->updatespec($lock)) {
753 $lock->log("Could not update spec file in order to create new " .
754 "update for `" . $self->name . "' package.\n");
755 return $lock->mark_failed;
759 if (!$lock->do($self->repodir, 'git', 'commit', '-a',
760 '-m', $self->message)) {
761 $lock->log("Could not commit changes into git repository `" .
762 $self->repodir . "'.\n");
763 return $lock->mark_failed;
766 if ($mode eq 'koji') {
768 if (!$lock->do($self->repodir, 'git', 'push')) {
769 $lock->log("Could not push changes from repository `" .
770 $self->repodir . "' to server.\n");
771 return $lock->mark_failed;
774 $lock->log("Not pushing changes because of local build mode.\n");
777 return $lock->mark_done;
781 # Submit package for building into Koji and store task ID.
782 # This is pointless in local build mode.
783 # Requires `clone'. Sould be called after `update'.
784 # Return true on success.
787 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
788 state => 'submitbuild');
789 if ($lock->is_done) { return 1; }
791 # Get NEVRA of intended build
792 my $nevra = $self->get_nevra_from_git($lock);
793 if (! defined $nevra) {
794 return $lock->mark_failed;
797 # Check the build is not already in Koji (e.g. by concurrent packager
798 # or after this program restart) but do not conclude anything.
800 if (!$lock->dooutput($self->repodir, \
$buildinfo, 'koji', 'buildinfo',
802 $lock->log("Could not ask Koji for `" . $nevra . "' status " .
803 "before submitting new build.\n");
804 return $lock->mark_failed;
806 if ($buildinfo =~ /No such build/m) {
807 $lock->log("Package not yet submitted for building as expected.\n");
809 # Get task ID of already building package
810 if ($buildinfo =~ /Task:\s*(\d+)/m) {
811 # TODO: We could compare task target and consider as submitted if
812 # equaled to intended target.
814 $lock->log("Package `$nevra' already submitted as task " .
815 "`$task_id'. Previous build failed or somebody builds the " .
816 "package concurrently.\n");
818 $lock->log("Package `$nevra' already in Koji, but task ID " .
819 "could not been determined.\n");
821 $lock->log("Re-submitting the package.\n")
826 if (!$lock->dooutput($self->repodir, \
$task_id, 'fedpkg',
827 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
828 'build', '--nowait', '--target', $self->target)) {
829 $lock->log("Could not submit `" . $nevra . "' into Koji.\n");
830 return $lock->mark_failed;
832 if (not $task_id =~ /Created task:\s*(\d+)/) {
833 $lock->log("Could not parse Koji task ID for `$nevra' build\n");
834 return $lock->mark_failed;
839 if (! $lock->nstorereference(\
$task_id, $self->taskstore)) {
840 $lock->log("Could not store task ID `" . $task_id . "' of `" . $nevra .
841 "' package into `" . $self->taskstore . "' file: $@\n");
842 return $lock->mark_failed;
844 $lock->log("Task ID `" . $task_id . "' stored into `" .
845 $self->taskstore . "' file sucessfully.\n");
848 return $lock->mark_done;
852 # If first argument is:
853 # "koji" wait for package build in Koji,
854 # "local" build locally using fedpkg.
855 # "mock" build locally using mock.
856 # Second argument is reference to array of repository URLs to use as package
858 # Third argument is architecture.
859 # base when building the package.
860 # Requires `clone'. Sould be called after `update' or `submitbuild'.
861 # Return true on success.
863 my ($self, $mode, $repositories, $architecture) = @_;
864 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
866 if ($lock->is_done) { return 1; }
868 my $nevra = $self->get_nevra_from_git($lock);
869 if (! defined $nevra) {
870 return $lock->mark_failed;
873 if ($mode eq 'local') {
874 if (!$lock->do($self->repodir, 'fedpkg',
875 (defined $self->pkgdist) ?
('--dist', $self->pkgdist) : (),
877 $lock->log("Could not build `" . $nevra . "' locally.\n");
878 return $lock->mark_failed;
880 } elsif ($mode eq 'koji') {
881 # Retrieve task ID of submitted build
882 my $task_id = $lock->retrievereference($self->taskstore);
884 $lock->log("Could not load task ID for `". $nevra .
885 "' build from `" . $self->taskstore . "' file: $@\n");
886 return $lock->mark_failed;
888 $task_id = $$task_id;
890 # Wait for build task result
891 # TODO: How to recognize the process died for other reason
892 # than build failure?
893 if (!$lock->do($self->repodir, 'koji', 'watch-task', $task_id)) {
894 $lock->log("Could not get status of Koji task `" . $task_id .
895 "' for `$nevra' build, or the task failed.\n");
896 return $lock->mark_failed;
898 } elsif ($mode eq 'mock') {
899 my $srpm_name = $self->get_srpm_name($lock, $mode, $repositories,
901 if (! defined $srpm_name) {
902 return $lock->mark_failed;
904 if (!replace_directory
($lock, $self->mockdir)) {
905 return $lock->mark_failed;
907 my ($mock_config_dir, $mock_config_root);
909 ($mock_config_dir, $mock_config_root) =
910 Fedora
::Rebuild
::Mock
->new(
911 architecture
=> $architecture,
912 repositories
=> $repositories
913 )->make_configuration;
916 $lock->log("Could not configure mock: $@\n");
917 File
::Path
::remove_tree
($mock_config_dir);
918 return $lock->mark_failed;
920 if (!$lock->do(undef, 'mock', '--resultdir', $self->mockdir,
921 '--configdir', $mock_config_dir, '--root', $mock_config_root,
922 '--rebuild', $srpm_name)) {
923 $lock->log("Could not build `" . $nevra . "' in mock.\n");
924 File
::Path
::remove_tree
($mock_config_dir);
925 return $lock->mark_failed;
927 File
::Path
::remove_tree
($mock_config_dir);
929 $lock->log("Could not build `" . $nevra .
930 "' because of unknown building mode `" . $mode . "'.\n");
931 return $lock->mark_failed;
934 return $lock->mark_done;
938 # Waits for build root rotation. Just built package will be available for
939 # other packages at build time after returning from this fuction.
940 # If first argument is "koji", it will wait for the Koji rotation.
941 # If first argument is "mock", it will create yum repository in directory with
943 # If first argument is "local", this function is void,
944 # Requires `update'. Sould be called after `build'.
945 # Return true on success.
946 sub dowaitforbuildroot
{
947 my ($self, $mode) = @_;
948 my $lock = Fedora
::Rebuild
::Package
::StateLock
->new(package => $self,
950 if ($lock->is_done) { return 1; }
952 my $nevra = $self->get_nevra_from_git($lock);
953 if (! defined $nevra) {
954 return $lock->mark_failed;
957 if ($mode eq 'koji') {
958 if (!$lock->do($self->repodir, 'koji', 'wait-repo',
959 '--build=' . $nevra, '--target', $self->target)) {
960 $lock->log("Koji does not contain `" . $nevra .
961 "' package in build root for `" . $self->target .
962 "' build target yet.\n");
963 return $lock->mark_failed;
965 } elsif ($mode eq 'mock') {
966 $lock->log("`" . $nevra .
967 "' built in mock, creating yum repository...\n");
968 if (!$lock->do($self->rpmdir, 'createrepo', '.')) {
969 $lock->log("Could not create yum repository for `" . $nevra .
971 return $lock->mark_failed;
973 $lock->log("`" . $nevra .
974 "' yum repository created successfully.\n");
976 $lock->log("`" . $nevra .
977 "' built locally, not waiting on Koji rotation.\n");
980 return $lock->mark_done;
983 # Set hash of build-time dependencies (requires attribute).
984 # If first argument is:
985 # "local" build SRPM locally using fedpkg,
986 # "koji" or "mock" build SRPM using mock.
987 # Second argument is reference to array of repository URLs to use as package
989 # Third argument is architecture.
990 # Fourth argument is anonymous clone boolean.
991 # Fifth argument is Mock object to use instead of fresh one (value undef).
992 # (This is safe only in single-threaded run. This is limitation of mock.)
993 # Return undef in case of failure.
994 sub get_buildrequires
{
995 my ($self, $mode, $repositories, $architecture, $anonymous, $mock) = @_;
997 print "Getting BuildRequires for `" . $self->name . "'...\n";
999 # Procede all steps, each must be re-doable
1000 $ok = $self->clone($anonymous);
1001 $ok = $self->srpm(0, $mode, $repositories, $architecture, $mock) if $ok;
1002 $ok = $self->storebuildrequires($mode, $repositories, $architecture) if $ok;
1003 $ok = $self->buildrequires if $ok;
1006 print "BuildRequires for `" . $self->name .
1007 "' package distilled successfully.\n";
1010 print "Could not get BuildRequires for `" . $self->name .
1016 # Set hash of run-time requires (Requires attribute).
1017 # Return true on success, undef in case of failure.
1018 # XXX: Requires `runrequiresstore'
1019 sub get_binaryrequires
{
1022 print "Getting run-time requires for `" . $self->name . "'...\n";
1024 $ok = $self->storebinaryrequires;
1025 $ok = $self->loadbinaryrequires if $ok;
1028 print "Run-time requires for `" . $self->name .
1029 "' package distilled successfully.\n";
1032 print "Could not get run-time requires for `" . $self->name .
1038 # Set hash of run-time provides (provides attribute).
1039 # Return true on success, undef in case of failure.
1040 # XXX: Requires `providesstore'
1041 sub get_binaryprovides
{
1044 print "Getting binary provides for `" . $self->name . "'...\n";
1046 $ok = $self->storebinaryprovides;
1047 $ok = $self->loadbinaryprovides if $ok;
1050 print "Provides for `" . $self->name .
1051 "' package distilled successfully.\n";
1054 print "Could not get Provides for `" . $self->name . "' package.\n";
1059 # Set hash of run-time requires (Requires attribute) and provides
1060 # (provides attribute). Common wrapper for getbinaryrequires() and
1061 # getbinaryprovides().
1062 # Return true on success, undef in case of failure.
1063 # XXX: Requires `runrequiresstore' and `providesstore'
1064 sub get_binarydependencies
{
1067 print "Getting binary dependencies for `" . $self->name . "'...\n";
1069 $ok = $self->get_binaryrequires;
1070 $ok = $self->get_binaryprovides if $ok;
1073 print "Binary dependencies for `" . $self->name .
1074 "' package distilled successfully.\n";
1077 print "Could not get binary dependencies for `" . $self->name .
1083 # Rebuild a package without waiting for propagation to next build root.
1084 # First argument defines build mode:
1085 # "koji" publish commit and build in Koji,
1086 # "local" build locally without pushing commits to server,
1087 # "mock" build in mock without pushing commits to server.
1088 # Second argument is reference to array of repository URLs to use as package
1089 # base when building the package.
1090 # Third argument is architecture.
1091 # Fourth argument is anonymous clone boolean.
1093 my ($self, $mode, $repositories, $architecture, $anonymous) = @_;
1095 print "Rebuilding `" . $self->name . "'...\n";
1097 # Proceed all steps, each must be re-doable
1098 $ok = $self->clone($anonymous);
1099 $ok = $self->update($mode) if $ok;
1100 $ok = $self->submitbuild if ($ok and $mode eq 'koji');
1101 $ok = $self->build($mode, $repositories, $architecture) if $ok;
1102 $ok = $self->binaryrpm($mode, $architecture) if $ok;
1103 $ok = $self->get_binaryprovides if $ok;
1104 $ok = $self->get_binaryrequires if $ok;
1107 print "`" . $self->name . "' rebuild finished successfully.\n";
1109 print "`" . $self->name . "' rebuild failed.\n";
1114 # Wait for the package propagated into new build root.
1115 # If first argument is "koji", waits for Koji build root rotation,
1116 # Otherwise waits locally (no-op).
1117 # XXX: Requires `update', should be called after rebuilding package.
1118 sub waitforbuildroot
{
1119 my ($self, $mode) = @_;
1121 print "Waiting for `" . $self->name . "' to get to build root...\n";
1123 $ok = $self->dowaitforbuildroot($mode);
1126 print "`" . $self->name . "' propagated successfully.\n";
1128 print "`" . $self->name . "' propagation failed.\n";