#
# SMOCK - Simpler Mock
# by Dan Berrange and Richard W.M. Jones.
+# Copyright (C) 2008 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
use strict;
my @arches = ();
my @distros = ();
+my $suffix = "";
my $localrepo = $ENV{HOME} . "/public_html/smock/yum";
+my $dryrun = 0;
+my $keepgoing = 0;
+my $chain = 0;
my $help = 0;
my $man = 0;
GetOptions (
"arch=s" => \@arches,
"distro=s" => \@distros,
+ "suffix=s" => \$suffix,
"localrepo=s" => \$localrepo,
+ "dryrun" => \$dryrun,
+ "keepgoing" => \$keepgoing,
+ "chain" => \$chain,
"help|?" => \$help,
"man" => \$man
) or pod2usage (2);
localrepo directory).
B<NOTE:> Please read the README file first. You need to set up mock
-and a web server before you can use this command.
+and optionally a web server before you can use this command.
=head1 OPTIONS
Local repository. Defaults to C<$HOME/public_html/smock/yum>
+=item B<--dryrun>
+
+Don't run any commands, just print the packages in the order
+in which they must be built.
+
+=item B<--keepgoing>
+
+Don't exit if a package fails, but keep building.
+
+Note that this isn't always safe because new packages may be built
+against older packages, in the case where the older package couldn't
+be rebuilt because of an error.
+
+However, it is very useful.
+
+=item B<--chain>
+
+Don't run any commands, just print the packages in the correct
+format for chain building. See:
+L<http://fedoraproject.org/wiki/Koji/UsingKoji#Chained_builds>
+
+=item B<--suffix>
+
+Append a suffix to the mock configuration file in order to use
+a custom one.
+
=back
=cut
open PIPE, "$_[0] |" or die "$_[0]: $!";
my @lines;
foreach (<PIPE>) {
- chomp;
- push @lines, $_;
+ chomp;
+ push @lines, $_;
}
close PIPE;
return @lines;
#print " buildrequires = ", join (",", @buildrequires), "\n";
$srpms{$name} = {
- name => $name,
- version => $version,
- release => $release,
- buildrequires => \@buildrequires,
- filename => $srpm
+ name => $name,
+ version => $version,
+ release => $release,
+ buildrequires => \@buildrequires,
+ filename => $srpm
}
}
my $item = shift;
foreach (@_) {
- return 1 if $item eq $_;
+ return 1 if $item eq $_;
+ }
+ 0;
+}
+
+sub dependency_in
+{
+ my $dep = shift; # eg. dbus-devel
+
+ while ($dep) {
+ return $dep if is_member_of ($dep, @_);
+ my $newdep = $dep;
+ $newdep =~ s/-\w+$//; # eg. dbus-devel -> dbus
+ last if $newdep eq $dep;
+ $dep = $newdep;
}
0;
}
-my @names = keys %srpms;
-foreach my $name (@names) {
+foreach my $name (keys %srpms) {
my @buildrequires = @{$srpms{$name}->{buildrequires}};
- @buildrequires = grep { is_member_of ($_, @names) } @buildrequires;
+ @buildrequires =
+ grep { $_ = dependency_in ($_, keys %srpms) } @buildrequires;
$srpms{$name}{buildrequires} = \@buildrequires;
}
-# Now sort the SRPMs into the correct order for building
+# This function takes a list of package names and sorts them into the
+# correct order for building, given the existing %srpms hash
+# containing buildrequires. We use the external 'tsort' program.
+
+sub tsort
+{
+ my @names = @_;
+
+ my ($fh, $filename) = tempfile ();
+
+ foreach my $name (@names) {
+ my @buildrequires = @{$srpms{$name}->{buildrequires}};
+ foreach (@buildrequires) {
+ print $fh "$_ $name\n"
+ }
+ # Add a self->self dependency. This ensures that any
+ # packages which don't have or appear as a dependency of
+ # any other package still get built.
+ print $fh "$name $name\n"
+ }
+ close $fh;
+
+ get_lines "tsort $filename";
+}
+
+# Sort the initial list of package names.
-my ($fh, $filename) = tempfile ();
+my @names = sort keys %srpms;
+my @buildorder = tsort (@names);
-foreach my $name (@names) {
- my @buildrequires = @{$srpms{$name}->{buildrequires}};
- foreach (@buildrequires) {
- print $fh "$_ $name\n"
+# With --chain flag we print the packages in groups for chain building.
+
+if ($chain) {
+ my %group = ();
+ my $name;
+
+ print 'make chain-build CHAIN="';
+
+ foreach $name (@buildorder) {
+ my @br = @{$srpms{$name}->{buildrequires}};
+
+ # If a BR occurs within the current group, then start the next group.
+ my $occurs = 0;
+ foreach (@br) {
+ if (exists $group{$_}) {
+ $occurs = 1;
+ last;
+ }
+ }
+
+ if ($occurs) {
+ %group = ();
+ print ": ";
+ }
+
+ $group{$name} = 1;
+ print "$name ";
}
+ print "\"\n";
+
+ exit 0
}
-close $fh;
-my @buildorder = get_lines "tsort $filename";
+# With --dryrun flag we just print the packages in build order then exit.
-#foreach (@buildorder) {
-# print "$_\n";
-#}
+if ($dryrun) {
+ foreach (@buildorder) {
+ print "$_\n";
+ }
+
+ exit 0
+}
# Now we can build each SRPM.
local $_ = $_[0];
if (! -d $_) {
- mkdir ($_, 0755) or die "mkdir $_: $!"
+ mkdir ($_, 0755) or die "mkdir $_: $!"
}
}
my_mkdir "$localrepo/$distro/src";
my_mkdir "$localrepo/$distro/src/SRPMS";
system ("cd $localrepo/$distro/src && rm -rf repodata && createrepo -q .") == 0
- or die "createrepo failed: $?\n";
+ or die "createrepo failed: $?\n";
my_mkdir "$localrepo/$distro/$arch";
my_mkdir "$localrepo/$distro/$arch/RPMS";
my_mkdir "$localrepo/$distro/$arch/logs";
system ("cd $localrepo/$distro/$arch && rm -rf repodata && createrepo -q --exclude 'logs/*rpm' .") == 0
- or die "createrepo failed: $?\n";
+ or die "createrepo failed: $?\n";
}
if (! -d "$localrepo/scratch") {
mkdir "$localrepo/scratch"
- or die "mkdir $localrepo/scratch: $!\nIf you haven't set up a local repository yet, you must read the README file.\n";
-}
-
-system "rm -f $localrepo/scratch/*";
-
-foreach my $name (@buildorder) {
- my $version = $srpms{$name}->{version};
- my $release = $srpms{$name}->{release};
- my $srpm_filename = $srpms{$name}->{filename};
-
- $release =~ s/\.fc?\d+$//; # "1.fc9" -> "1"
-
- foreach my $arch (@arches) {
- foreach my $distro (@distros) {
- # Does the built (binary) package exist already?
- my $pattern = "$localrepo/$distro/$arch/RPMS/$name-$version-$release.*.rpm";
- #print "pattern = $pattern\n";
- my @binaries = glob $pattern;
-
- if (@binaries == 0)
- {
- # Rebuild the package.
- print "*** building $name-$version-$release $arch $distro ***\n";
-
- createrepo ($arch, $distro);
- system ("mock -r $distro-$arch --resultdir $localrepo/scratch $srpm_filename") == 0
- or die "Build failed, return code $?\nLeaving the logs in $localrepo/scratch\n";
-
- # Build was a success so move the final RPMs into the
- # mock repo for next time.
- system ("mv $localrepo/scratch/*.src.rpm $localrepo/$distro/src/SRPMS") == 0 or die "mv";
- system ("mv $localrepo/scratch/*.rpm $localrepo/$distro/$arch/RPMS") == 0 or die "mv";
- my_mkdir "$localrepo/$distro/$arch/logs/$name-$version-$release";
- system ("mv $localrepo/scratch/*.log $localrepo/$distro/$arch/logs/$name-$version-$release/") == 0 or die "mv";
-
- createrepo ($arch, $distro);
- }
- else
- {
- print "skipping $name-$version-$release $arch $distro\n";
- }
- }
+ or die "mkdir $localrepo/scratch: $!\nIf you haven't set up a local repository yet, you must read the README file.\n";
+}
+
+system "rm -rf $localrepo/scratch/*";
+
+my @errors = ();
+
+# NB: Need to do the arch/distro in the outer loop to work
+# around the caching bug in mock/yum.
+foreach my $arch (@arches) {
+ foreach my $distro (@distros) {
+ foreach my $name (@buildorder) {
+ my $version = $srpms{$name}->{version};
+ my $release = $srpms{$name}->{release};
+ my $srpm_filename = $srpms{$name}->{filename};
+
+ $release =~ s/\.fc?\d+$//; # "1.fc9" -> "1"
+
+ # Does the built (binary) package exist already?
+ my $pattern = "$localrepo/$distro/$arch/RPMS/$name-$version-$release.*.rpm";
+ #print "pattern = $pattern\n";
+ my @binaries = glob $pattern;
+
+ if (@binaries == 0)
+ {
+ # Rebuild the package.
+ print "*** building $name-$version-$release $arch $distro ***\n";
+
+ createrepo ($arch, $distro);
+
+ my $scratchdir = "$localrepo/scratch/$name-$distro-$arch";
+ mkdir $scratchdir;
+
+ if (system ("mock -r $distro-$arch$suffix --resultdir $scratchdir $srpm_filename") == 0) {
+ # Build was a success so move the final RPMs into the
+ # mock repo for next time.
+ system ("mv $scratchdir/*.src.rpm $localrepo/$distro/src/SRPMS") == 0 or die "mv";
+ system ("mv $scratchdir/*.rpm $localrepo/$distro/$arch/RPMS") == 0 or die "mv";
+ my_mkdir "$localrepo/$distro/$arch/logs/$name-$version-$release";
+ system ("mv $scratchdir/*.log $localrepo/$distro/$arch/logs/$name-$version-$release/") == 0 or die "mv";
+ system "rm -rf $scratchdir";
+
+ createrepo ($arch, $distro);
+
+ }
+ else {
+ push @errors, "$name-$distro-$arch$suffix";
+ print STDERR "Build failed, return code $?\nLeaving the logs in $scratchdir\n";
+ exit 1 unless $keepgoing;
+ }
+ }
+ else
+ {
+ print "skipping $name-$version-$release $arch $distro\n";
+ }
+ }
}
}
+
+if (@errors) {
+ print "\n\n\nBuild failed for the following packages:\n";
+ print " $_\n" foreach @errors;
+ exit 1
+}
+
+exit 0