ed321628f7ee3eb09e77172a532e6989346709e9
[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 my @names = keys %srpms;
171 foreach my $name (@names) {
172     my @buildrequires = @{$srpms{$name}->{buildrequires}};
173     @buildrequires = grep { is_member_of ($_, @names) } @buildrequires;
174     $srpms{$name}{buildrequires} = \@buildrequires;
175 }
176
177 # Now sort the SRPMs into the correct order for building
178
179 my ($fh, $filename) = tempfile ();
180
181 foreach my $name (@names) {
182     my @buildrequires = @{$srpms{$name}->{buildrequires}};
183     foreach (@buildrequires) {
184         print $fh "$_ $name\n"
185     }
186 }
187 close $fh;
188
189 my @buildorder = get_lines "tsort $filename";
190
191 #foreach (@buildorder) {
192 #    print "$_\n";
193 #}
194
195 # Now we can build each SRPM.
196
197 sub my_mkdir
198 {
199     local $_ = $_[0];
200
201     if (! -d $_) {
202         mkdir ($_, 0755) or die "mkdir $_: $!"
203     }
204 }
205
206 sub createrepo
207 {
208     my $arch = shift;
209     my $distro = shift;
210
211     my_mkdir "$localrepo/$distro";
212     my_mkdir "$localrepo/$distro/src";
213     my_mkdir "$localrepo/$distro/src/SRPMS";
214     system ("cd $localrepo/$distro/src && rm -rf repodata && createrepo -q .") == 0
215         or die "createrepo failed: $?\n";
216
217     my_mkdir "$localrepo/$distro/$arch";
218     my_mkdir "$localrepo/$distro/$arch/RPMS";
219     my_mkdir "$localrepo/$distro/$arch/logs";
220
221     system ("cd $localrepo/$distro/$arch && rm -rf repodata && createrepo -q --exclude 'logs/*rpm' .") == 0
222         or die "createrepo failed: $?\n";
223 }
224
225 if (! -d "$localrepo/scratch") {
226     mkdir "$localrepo/scratch"
227         or die "mkdir $localrepo/scratch: $!\nIf you haven't set up a local repository yet, you must read the README file.\n";
228 }
229
230 system "rm -f $localrepo/scratch/*";
231
232 foreach my $name (@buildorder) {
233     my $version = $srpms{$name}->{version};
234     my $release = $srpms{$name}->{release};
235     my $srpm_filename = $srpms{$name}->{filename};
236
237     $release =~ s/\.fc?\d+$//; # "1.fc9" -> "1"
238
239     foreach my $arch (@arches) {
240         foreach my $distro (@distros) {
241             # Does the built (binary) package exist already?
242             my $pattern = "$localrepo/$distro/$arch/RPMS/$name-$version-$release.*.rpm";
243             #print "pattern = $pattern\n";
244             my @binaries = glob $pattern;
245
246             if (@binaries == 0)
247             {
248                 # Rebuild the package.
249                 print "*** building $name-$version-$release $arch $distro ***\n";
250
251                 createrepo ($arch, $distro);
252                 system ("mock -r $distro-$arch --resultdir $localrepo/scratch $srpm_filename") == 0
253                     or die "Build failed, return code $?\nLeaving the logs in $localrepo/scratch\n";
254
255                 # Build was a success so move the final RPMs into the
256                 # mock repo for next time.
257                 system ("mv $localrepo/scratch/*.src.rpm $localrepo/$distro/src/SRPMS") == 0 or die "mv";
258                 system ("mv $localrepo/scratch/*.rpm $localrepo/$distro/$arch/RPMS") == 0 or die "mv";
259                 my_mkdir "$localrepo/$distro/$arch/logs/$name-$version-$release";
260                 system ("mv $localrepo/scratch/*.log $localrepo/$distro/$arch/logs/$name-$version-$release/") == 0 or die "mv";
261
262                 createrepo ($arch, $distro);
263             }
264             else
265             {
266                 print "skipping $name-$version-$release $arch $distro\n";
267             }
268         }
269     }
270 }