4 # by Dan Berrange and Richard W.M. Jones.
5 # Copyright (C) 2008 Red Hat Inc.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 use File::Temp qw(tempfile);
30 my $localrepo = $ENV{HOME} . "/public_html/smock/yum";
39 "distro=s" => \@distros,
40 "suffix=s" => \$suffix,
41 "localrepo=s" => \$localrepo,
43 "keepgoing" => \$keepgoing,
48 pod2usage (1) if $help;
49 pod2usage (-exitstatus => 0, -verbose => 2) if $man;
59 smock.pl --arch=i386 --arch=x86_64 --distro=fedora-10 list of SRPMs ...
63 This is a wrapper around I<mock> which lets you build a whole group of
64 mutually dependent SRPMs in one go.
66 The smock command will work out the correct order in which to build
67 the SRPMs, and makes the result of previous RPM builds available as
68 dependencies for later builds.
70 Smock also works incrementally. It won't rebuild RPMs which were
71 built already in a previous run, which means if a package fails to
72 build, you can just fix it and rerun the same smock command. (In the
73 unlikely case that you want to force smock to rebuild RPMs then you
74 must bump the release number or delete the binary RPM from the
77 B<NOTE:> Please read the README file first. You need to set up mock
78 and a web server before you can use this command.
86 Specify the architecture(s) to build, eg. i386, x86_64. You can
87 list this option several times to build several architectures.
91 Specify the distribution(s) to build, eg. fedora-9, fedora-10.
92 You can list this option several times to build several distributions.
96 Local repository. Defaults to C<$HOME/public_html/smock/yum>
100 Don't run any commands, just print the packages in the order
101 in which they must be built.
105 Don't exit if a package fails, but keep building.
107 Note that this isn't always safe because new packages may be built
108 against older packages, in the case where the older package couldn't
109 be rebuilt because of an error.
111 However, it is very useful.
115 Don't run any commands, just print the packages in the correct
116 format for chain building. See:
117 L<http://fedoraproject.org/wiki/Koji/UsingKoji#Chained_builds>
121 Append a suffix to the mock configuration file in order to use
131 die "smock: specify one or more architectures using --arch=<arch>\n"
135 die "smock: specify one or more distros using --distro=<distro>\n"
139 die "smock: specify one or more SRPMs to build on the command line\n"
142 # Resolve the names, dependency list, etc. of the SRPMs that were
147 open PIPE, "$_[0] |" or die "$_[0]: $!";
157 open PIPE, "$_[0] |" or die "$_[0]: $!";
168 foreach my $srpm (@srpms) {
169 my $name = get_one_line "rpm -q --qf '%{name}' -p '$srpm'";
170 my $version = get_one_line "rpm -q --qf '%{version}' -p '$srpm'";
171 my $release = get_one_line "rpm -q --qf '%{release}' -p '$srpm'";
173 my @buildrequires = get_lines "rpm -q --requires -p '$srpm' |
174 grep -Eo '^[^[:space:]]+'";
176 #print "Filename: $srpm\n";
177 #print " name = $name\n";
178 #print " version = $version\n";
179 #print " release = $release\n";
180 #print " buildrequires = ", join (",", @buildrequires), "\n";
186 buildrequires => \@buildrequires,
191 # We don't care about buildrequires unless they refer to other
192 # packages that we are building. So filter them on this condition.
199 return 1 if $item eq $_;
206 my $dep = shift; # eg. dbus-devel
209 return $dep if is_member_of ($dep, @_);
211 $newdep =~ s/-\w+$//; # eg. dbus-devel -> dbus
212 last if $newdep eq $dep;
218 foreach my $name (keys %srpms) {
219 my @buildrequires = @{$srpms{$name}->{buildrequires}};
221 grep { $_ = dependency_in ($_, keys %srpms) } @buildrequires;
222 $srpms{$name}{buildrequires} = \@buildrequires;
225 # This function takes a list of package names and sorts them into the
226 # correct order for building, given the existing %srpms hash
227 # containing buildrequires. We use the external 'tsort' program.
233 my ($fh, $filename) = tempfile ();
235 foreach my $name (@names) {
236 my @buildrequires = @{$srpms{$name}->{buildrequires}};
237 foreach (@buildrequires) {
238 print $fh "$_ $name\n"
240 # Add a self->self dependency. This ensures that any
241 # packages which don't have or appear as a dependency of
242 # any other package still get built.
243 print $fh "$name $name\n"
247 get_lines "tsort $filename";
250 # Sort the initial list of package names.
252 my @names = sort keys %srpms;
253 my @buildorder = tsort (@names);
255 # With --chain flag we print the packages in groups for chain building.
261 print 'make chain-build CHAIN="';
263 foreach $name (@buildorder) {
264 my @br = @{$srpms{$name}->{buildrequires}};
266 # If a BR occurs within the current group, then start the next group.
269 if (exists $group{$_}) {
288 # With --dryrun flag we just print the packages in build order then exit.
291 foreach (@buildorder) {
298 # Now we can build each SRPM.
305 mkdir ($_, 0755) or die "mkdir $_: $!"
314 my_mkdir "$localrepo/$distro";
315 my_mkdir "$localrepo/$distro/src";
316 my_mkdir "$localrepo/$distro/src/SRPMS";
317 system ("cd $localrepo/$distro/src && rm -rf repodata && createrepo -q .") == 0
318 or die "createrepo failed: $?\n";
320 my_mkdir "$localrepo/$distro/$arch";
321 my_mkdir "$localrepo/$distro/$arch/RPMS";
322 my_mkdir "$localrepo/$distro/$arch/logs";
324 system ("cd $localrepo/$distro/$arch && rm -rf repodata && createrepo -q --exclude 'logs/*rpm' .") == 0
325 or die "createrepo failed: $?\n";
328 if (! -d "$localrepo/scratch") {
329 mkdir "$localrepo/scratch"
330 or die "mkdir $localrepo/scratch: $!\nIf you haven't set up a local repository yet, you must read the README file.\n";
333 system "rm -rf $localrepo/scratch/*";
337 # NB: Need to do the arch/distro in the outer loop to work
338 # around the caching bug in mock/yum.
339 foreach my $arch (@arches) {
340 foreach my $distro (@distros) {
341 foreach my $name (@buildorder) {
342 my $version = $srpms{$name}->{version};
343 my $release = $srpms{$name}->{release};
344 my $srpm_filename = $srpms{$name}->{filename};
346 $release =~ s/\.fc?\d+$//; # "1.fc9" -> "1"
348 # Does the built (binary) package exist already?
349 my $pattern = "$localrepo/$distro/$arch/RPMS/$name-$version-$release.*.rpm";
350 #print "pattern = $pattern\n";
351 my @binaries = glob $pattern;
355 # Rebuild the package.
356 print "*** building $name-$version-$release $arch $distro ***\n";
358 createrepo ($arch, $distro);
360 my $scratchdir = "$localrepo/scratch/$name-$distro-$arch";
363 if (system ("mock -r $distro-$arch$suffix --resultdir $scratchdir $srpm_filename") == 0) {
364 # Build was a success so move the final RPMs into the
365 # mock repo for next time.
366 system ("mv $scratchdir/*.src.rpm $localrepo/$distro/src/SRPMS") == 0 or die "mv";
367 system ("mv $scratchdir/*.rpm $localrepo/$distro/$arch/RPMS") == 0 or die "mv";
368 my_mkdir "$localrepo/$distro/$arch/logs/$name-$version-$release";
369 system ("mv $scratchdir/*.log $localrepo/$distro/$arch/logs/$name-$version-$release/") == 0 or die "mv";
370 system "rm -rf $scratchdir";
372 createrepo ($arch, $distro);
376 push @errors, "$name-$distro-$arch$suffix";
377 print STDERR "Build failed, return code $?\nLeaving the logs in $scratchdir\n";
378 exit 1 unless $keepgoing;
383 print "skipping $name-$version-$release $arch $distro\n";
390 print "\n\n\nBuild failed for the following packages:\n";
391 print " $_\n" foreach @errors;