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);
33 my $localrepo = $ENV{HOME} . "/public_html/smock/yum";
41 "distro=s" => \@distros,
44 "keepgoing" => \$keepgoing,
45 "localrepo=s" => \$localrepo,
47 "overwrite" => \$overwrite,
48 "suffix=s" => \$suffix,
50 pod2usage (1) if $help;
51 pod2usage (-exitstatus => 0, -verbose => 2) if $man;
61 smock.pl --arch=i386 --arch=x86_64 --distro=fedora-10 list of SRPMs ...
65 This is a wrapper around I<mock> which lets you build a whole group of
66 mutually dependent SRPMs in one go.
68 The smock command will work out the correct order in which to build
69 the SRPMs, and makes the result of previous RPM builds available as
70 dependencies for later builds.
72 Smock also works incrementally. It won't rebuild RPMs which were
73 built already in a previous run, which means if a package fails to
74 build, you can just fix it and rerun the same smock command. (In the
75 unlikely case that you want to force smock to rebuild RPMs then you
76 must bump the release number or delete the binary RPM from the
79 B<NOTE:> Please read the README file first. You need to set up mock
80 and optionally a web server before you can use this command.
88 Specify the architecture(s) to build, eg. i386, x86_64. You can
89 list this option several times to build several architectures.
93 Don't run any commands, just print the packages in the correct
94 format for chain building. See:
95 L<http://fedoraproject.org/wiki/Koji/UsingKoji#Chained_builds>
99 Specify the distribution(s) to build, eg. fedora-9, fedora-10.
100 You can list this option several times to build several distributions.
104 Don't run any commands, just print the packages in the order
105 in which they must be built.
113 Don't exit if a package fails, but keep building.
115 Note that this isn't always safe because new packages may be built
116 against older packages, in the case where the older package couldn't
117 be rebuilt because of an error.
119 However, it is very useful.
123 Local repository. Defaults to C<$HOME/public_html/smock/yum>
127 Show this help using man.
131 Overwrite existing files that are already in the repository. By default the
132 build of an SRPM is skipped if there is already a package with the same name,
133 version and release in the localrepo. With this option, the new build
134 overwrites the old one. This may lead to unexpected results, if the new build
135 does not create the same subpackages as the old one, because then the old
136 subpackages will still be accessible in the repository.
140 Append a suffix to the mock configuration file in order to use
150 die "smock: specify one or more architectures using --arch=<arch>\n"
154 die "smock: specify one or more distros using --distro=<distro>\n"
158 die "smock: specify one or more SRPMs to build on the command line\n"
161 # Resolve the names, dependency list, etc. of the SRPMs that were
166 open PIPE, "$_[0] |" or die "$_[0]: $!";
176 open PIPE, "$_[0] |" or die "$_[0]: $!";
187 foreach my $srpm (@srpms) {
188 my $name = get_one_line "rpm -q --qf '%{name}' -p '$srpm'";
189 my $version = get_one_line "rpm -q --qf '%{version}' -p '$srpm'";
190 my $release = get_one_line "rpm -q --qf '%{release}' -p '$srpm'";
192 my @buildrequires = get_lines "rpm -q --requires -p '$srpm' |
193 grep -Eo '^[^[:space:]]+'";
195 #print "Filename: $srpm\n";
196 #print " name = $name\n";
197 #print " version = $version\n";
198 #print " release = $release\n";
199 #print " buildrequires = ", join (",", @buildrequires), "\n";
205 buildrequires => \@buildrequires,
210 # We don't care about buildrequires unless they refer to other
211 # packages that we are building. So filter them on this condition.
218 return 1 if $item eq $_;
225 my $dep = shift; # eg. dbus-devel
228 return $dep if is_member_of ($dep, @_);
230 $newdep =~ s/-\w+$//; # eg. dbus-devel -> dbus
231 last if $newdep eq $dep;
237 foreach my $name (keys %srpms) {
238 my @buildrequires = @{$srpms{$name}->{buildrequires}};
240 grep { $_ = dependency_in ($_, keys %srpms) } @buildrequires;
241 $srpms{$name}{buildrequires} = \@buildrequires;
244 # This function takes a list of package names and sorts them into the
245 # correct order for building, given the existing %srpms hash
246 # containing buildrequires. We use the external 'tsort' program.
252 my ($fh, $filename) = tempfile ();
254 foreach my $name (@names) {
255 my @buildrequires = @{$srpms{$name}->{buildrequires}};
256 foreach (@buildrequires) {
257 print $fh "$_ $name\n"
259 # Add a self->self dependency. This ensures that any
260 # packages which don't have or appear as a dependency of
261 # any other package still get built.
262 print $fh "$name $name\n"
266 get_lines "tsort $filename";
269 # Sort the initial list of package names.
271 my @names = sort keys %srpms;
272 my @buildorder = tsort (@names);
274 # With --chain flag we print the packages in groups for chain building.
280 print 'make chain-build CHAIN="';
282 foreach $name (@buildorder) {
283 my @br = @{$srpms{$name}->{buildrequires}};
285 # If a BR occurs within the current group, then start the next group.
288 if (exists $group{$_}) {
307 # With --dryrun flag we just print the packages in build order then exit.
310 foreach (@buildorder) {
317 # Now we can build each SRPM.
324 mkdir ($_, 0755) or die "mkdir $_: $!"
333 my_mkdir "$localrepo/$distro";
334 my_mkdir "$localrepo/$distro/src";
335 my_mkdir "$localrepo/$distro/src/SRPMS";
336 system ("cd $localrepo/$distro/src && rm -rf repodata && createrepo -q .") == 0
337 or die "createrepo failed: $?\n";
339 my_mkdir "$localrepo/$distro/$arch";
340 my_mkdir "$localrepo/$distro/$arch/RPMS";
341 my_mkdir "$localrepo/$distro/$arch/logs";
343 system ("cd $localrepo/$distro/$arch && rm -rf repodata && createrepo -q --exclude 'logs/*rpm' .") == 0
344 or die "createrepo failed: $?\n";
347 if (! -d "$localrepo/scratch") {
348 mkdir "$localrepo/scratch"
349 or die "mkdir $localrepo/scratch: $!\nIf you haven't set up a local repository yet, you must read the README file.\n";
352 system "rm -rf $localrepo/scratch/*";
356 # NB: Need to do the arch/distro in the outer loop to work
357 # around the caching bug in mock/yum.
358 foreach my $arch (@arches) {
359 foreach my $distro (@distros) {
360 foreach my $name (@buildorder) {
361 my $version = $srpms{$name}->{version};
362 my $release = $srpms{$name}->{release};
363 my $srpm_filename = $srpms{$name}->{filename};
365 $release =~ s/\.fc?\d+$//; # "1.fc9" -> "1"
367 # Does the built (binary) package exist already?
368 my $pattern = "$localrepo/$distro/$arch/RPMS/$name-$version-$release.*.rpm";
369 #print "pattern = $pattern\n";
370 my @binaries = glob $pattern;
372 if (@binaries != 0 && $overwrite) {
373 print "*** overwriting $name-$version-$release $arch $distro ***\n";
376 if (@binaries == 0 || $overwrite)
378 # Rebuild the package.
379 print "*** building $name-$version-$release $arch $distro ***\n";
381 createrepo ($arch, $distro);
383 my $scratchdir = "$localrepo/scratch/$name-$distro-$arch";
386 if (system ("mock -r $distro-$arch$suffix --resultdir $scratchdir $srpm_filename") == 0) {
387 # Build was a success so move the final RPMs into the
388 # mock repo for next time.
389 system ("mv $scratchdir/*.src.rpm $localrepo/$distro/src/SRPMS") == 0 or die "mv";
390 system ("mv $scratchdir/*.rpm $localrepo/$distro/$arch/RPMS") == 0 or die "mv";
391 my_mkdir "$localrepo/$distro/$arch/logs/$name-$version-$release";
392 system ("mv $scratchdir/*.log $localrepo/$distro/$arch/logs/$name-$version-$release/") == 0 or die "mv";
393 system "rm -rf $scratchdir";
395 createrepo ($arch, $distro);
399 push @errors, "$name-$distro-$arch$suffix";
400 print STDERR "Build failed, return code $?\nLeaving the logs in $scratchdir\n";
401 exit 1 unless $keepgoing;
406 print "skipping $name-$version-$release $arch $distro\n";
413 print "\n\n\nBuild failed for the following packages:\n";
414 print " $_\n" foreach @errors;