Try to keep the names closer to alphabetical order.
[fedora-mingw.git] / smock / smock.pl
1 #!/usr/bin/perl -w
2 #
3 # SMOCK - Simpler Mock
4 # by Dan Berrange and Richard W.M. Jones.
5 # Copyright (C) 2008 Red Hat Inc.
6 #
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.
11 #
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.
16 #
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.
20
21 use strict;
22
23 use Getopt::Long;
24 use Pod::Usage;
25 use File::Temp qw(tempfile);
26
27 my @arches = ();
28 my @distros = ();
29 my $localrepo = $ENV{HOME} . "/public_html/smock/yum";
30 my $help = 0;
31 my $man = 0;
32
33 GetOptions (
34     "arch=s" => \@arches,
35     "distro=s" => \@distros,
36     "localrepo=s" => \$localrepo,
37     "help|?" => \$help,
38     "man" => \$man
39     ) or pod2usage (2);
40 pod2usage (1) if $help;
41 pod2usage (-exitstatus => 0, -verbose => 2) if $man;
42
43 =pod
44
45 =head1 NAME
46
47  smock - Simpler mock
48
49 =head1 SYNOPSIS
50
51  smock.pl --arch=i386 --arch=x86_64 --distro=fedora-10 list of SRPMs ...
52
53 =head1 DESCRIPTION
54
55 This is a wrapper around I<mock> which lets you build a whole group of
56 mutually dependent SRPMs in one go.
57
58 The smock command will work out the correct order in which to build
59 the SRPMs, and makes the result of previous RPM builds available as
60 dependencies for later builds.
61
62 Smock also works incrementally.  It won't rebuild RPMs which were
63 built already in a previous run, which means if a package fails to
64 build, you can just fix it and rerun the same smock command.  (In the
65 unlikely case that you want to force smock to rebuild RPMs then you
66 must bump the release number or delete the binary RPM from the
67 localrepo directory).
68
69 B<NOTE:> Please read the README file first.  You need to set up mock
70 and a web server before you can use this command.
71
72 =head1 OPTIONS
73
74 =over 4
75
76 =item B<--arch>
77
78 Specify the architecture(s) to build, eg. i386, x86_64.  You can
79 list this option several times to build several architectures.
80
81 =item B<--distro>
82
83 Specify the distribution(s) to build, eg. fedora-9, fedora-10.
84 You can list this option several times to build several distributions.
85
86 =item B<--localrepo>
87
88 Local repository.  Defaults to C<$HOME/public_html/smock/yum>
89
90 =back
91
92 =cut
93
94 my @srpms = @ARGV;
95
96 if (0 == @arches) {
97     die "smock: specify one or more architectures using --arch=<arch>\n"
98 }
99
100 if (0 == @distros) {
101     die "smock: specify one or more distros using --distro=<distro>\n"
102 }
103
104 if (0 == @srpms) {
105     die "smock: specify one or more SRPMs to build on the command line\n"
106 }
107
108 # Resolve the names, dependency list, etc. of the SRPMs that were
109 # specified.
110
111 sub get_one_line
112 {
113     open PIPE, "$_[0] |" or die "$_[0]: $!";
114     my $line = <PIPE>;
115     chomp $line;
116     close PIPE;
117     return $line;
118 }
119
120 sub get_lines
121 {
122     local $_;
123     open PIPE, "$_[0] |" or die "$_[0]: $!";
124     my @lines;
125     foreach (<PIPE>) {
126         chomp;
127         push @lines, $_;
128     }
129     close PIPE;
130     return @lines;
131 }
132
133 my %srpms = ();
134 foreach my $srpm (@srpms) {
135     my $name = get_one_line "rpm -q --qf '%{name}' -p '$srpm'";
136     my $version = get_one_line "rpm -q --qf '%{version}' -p '$srpm'";
137     my $release = get_one_line "rpm -q --qf '%{release}' -p '$srpm'";
138
139     my @buildrequires = get_lines "rpm -q --requires -p '$srpm' |
140         grep -Eo '^[^[:space:]]+'";
141
142     #print "Filename: $srpm\n";
143     #print "  name          = $name\n";
144     #print "  version       = $version\n";
145     #print "  release       = $release\n";
146     #print "  buildrequires = ", join (",", @buildrequires), "\n";
147
148     $srpms{$name} = {
149         name => $name,
150         version => $version,
151         release => $release,
152         buildrequires => \@buildrequires,
153         filename => $srpm
154     }
155 }
156
157 # We don't care about buildrequires unless they refer to other
158 # packages that we are building.  So filter them on this condition.
159
160 sub is_member_of
161 {
162     my $item = shift;
163
164     foreach (@_) {
165         return 1 if $item eq $_;
166     }
167     0;
168 }
169
170 sub dependency_in
171 {
172     my $dep = shift;            # eg. dbus-devel
173
174     while ($dep) {
175         return $dep if is_member_of ($dep, @_);
176         my $newdep = $dep;
177         $newdep =~ s/-\w+$//;   # eg. dbus-devel -> dbus
178         last if $newdep eq $dep;
179         $dep = $newdep;
180     }
181     0;
182 }
183
184 my @names = sort keys %srpms;
185 foreach my $name (@names) {
186     my @buildrequires = @{$srpms{$name}->{buildrequires}};
187     @buildrequires = grep { $_ = dependency_in ($_, @names) } @buildrequires;
188     $srpms{$name}{buildrequires} = \@buildrequires;
189 }
190
191 # Now sort the SRPMs into the correct order for building
192
193 my ($fh, $filename) = tempfile ();
194
195 foreach my $name (@names) {
196     my @buildrequires = @{$srpms{$name}->{buildrequires}};
197     foreach (@buildrequires) {
198         print $fh "$_ $name\n"
199     }
200 }
201 close $fh;
202
203 my @buildorder = get_lines "tsort $filename";
204
205 #foreach (@buildorder) {
206 #    print "$_\n";
207 #}
208
209 # Now we can build each SRPM.
210
211 sub my_mkdir
212 {
213     local $_ = $_[0];
214
215     if (! -d $_) {
216         mkdir ($_, 0755) or die "mkdir $_: $!"
217     }
218 }
219
220 sub createrepo
221 {
222     my $arch = shift;
223     my $distro = shift;
224
225     my_mkdir "$localrepo/$distro";
226     my_mkdir "$localrepo/$distro/src";
227     my_mkdir "$localrepo/$distro/src/SRPMS";
228     system ("cd $localrepo/$distro/src && rm -rf repodata && createrepo -q .") == 0
229         or die "createrepo failed: $?\n";
230
231     my_mkdir "$localrepo/$distro/$arch";
232     my_mkdir "$localrepo/$distro/$arch/RPMS";
233     my_mkdir "$localrepo/$distro/$arch/logs";
234
235     system ("cd $localrepo/$distro/$arch && rm -rf repodata && createrepo -q --exclude 'logs/*rpm' .") == 0
236         or die "createrepo failed: $?\n";
237 }
238
239 if (! -d "$localrepo/scratch") {
240     mkdir "$localrepo/scratch"
241         or die "mkdir $localrepo/scratch: $!\nIf you haven't set up a local repository yet, you must read the README file.\n";
242 }
243
244 system "rm -f $localrepo/scratch/*";
245
246 # NB: Need to do the arch/distro in the outer loop to work
247 # around the caching bug in mock/yum.
248 foreach my $arch (@arches) {
249     foreach my $distro (@distros) {
250         foreach my $name (@buildorder) {
251             my $version = $srpms{$name}->{version};
252             my $release = $srpms{$name}->{release};
253             my $srpm_filename = $srpms{$name}->{filename};
254
255             $release =~ s/\.fc?\d+$//; # "1.fc9" -> "1"
256
257             # Does the built (binary) package exist already?
258             my $pattern = "$localrepo/$distro/$arch/RPMS/$name-$version-$release.*.rpm";
259             #print "pattern = $pattern\n";
260             my @binaries = glob $pattern;
261
262             if (@binaries == 0)
263             {
264                 # Rebuild the package.
265                 print "*** building $name-$version-$release $arch $distro ***\n";
266
267                 createrepo ($arch, $distro);
268                 system ("mock -r $distro-$arch --resultdir $localrepo/scratch $srpm_filename") == 0
269                     or die "Build failed, return code $?\nLeaving the logs in $localrepo/scratch\n";
270
271                 # Build was a success so move the final RPMs into the
272                 # mock repo for next time.
273                 system ("mv $localrepo/scratch/*.src.rpm $localrepo/$distro/src/SRPMS") == 0 or die "mv";
274                 system ("mv $localrepo/scratch/*.rpm $localrepo/$distro/$arch/RPMS") == 0 or die "mv";
275                 my_mkdir "$localrepo/$distro/$arch/logs/$name-$version-$release";
276                 system ("mv $localrepo/scratch/*.log $localrepo/$distro/$arch/logs/$name-$version-$release/") == 0 or die "mv";
277
278                 createrepo ($arch, $distro);
279             }
280             else
281             {
282                 print "skipping $name-$version-$release $arch $distro\n";
283             }
284         }
285     }
286 }