smock: substitute tab with 4 blanks for uniformity
[fedora-mingw.git] / smock / smock.pl
index d7ebbc5..fd5f17a 100755 (executable)
@@ -26,14 +26,22 @@ use File::Temp qw(tempfile);
 
 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);
@@ -87,6 +95,32 @@ You can list this option several times to build several distributions.
 
 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
@@ -123,8 +157,8 @@ sub get_lines
     open PIPE, "$_[0] |" or die "$_[0]: $!";
     my @lines;
     foreach (<PIPE>) {
-       chomp;
-       push @lines, $_;
+    chomp;
+    push @lines, $_;
     }
     close PIPE;
     return @lines;
@@ -146,11 +180,11 @@ foreach my $srpm (@srpms) {
     #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
     }
 }
 
@@ -162,49 +196,104 @@ sub is_member_of
     my $item = shift;
 
     foreach (@_) {
-       return 1 if $item eq $_;
+    return 1 if $item eq $_;
     }
     0;
 }
 
 sub dependency_in
 {
-    my $dep = shift;           # eg. dbus-devel
+    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;
+    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 { $_ = dependency_in ($_, @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 ();
+    my ($fh, $filename) = tempfile ();
 
-foreach my $name (@names) {
+    foreach my $name (@names) {
     my @buildrequires = @{$srpms{$name}->{buildrequires}};
     foreach (@buildrequires) {
-       print $fh "$_ $name\n"
+        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";
 }
-close $fh;
 
-my @buildorder = get_lines "tsort $filename";
+# Sort the initial list of package names.
+
+my @names = sort keys %srpms;
+my @buildorder = tsort (@names);
+
+# With --chain flag we print the packages in groups for chain building.
+
+if ($chain) {
+    my %group = ();
+    my $name;
+
+    print 'make chain-build CHAIN="';
 
-#foreach (@buildorder) {
-#    print "$_\n";
-#}
+    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
+}
+
+# With --dryrun flag we just print the packages in build order then exit.
+
+if ($dryrun) {
+    foreach (@buildorder) {
+    print "$_\n";
+    }
+
+    exit 0
+}
 
 # Now we can build each SRPM.
 
@@ -213,7 +302,7 @@ sub my_mkdir
     local $_ = $_[0];
 
     if (! -d $_) {
-       mkdir ($_, 0755) or die "mkdir $_: $!"
+    mkdir ($_, 0755) or die "mkdir $_: $!"
     }
 }
 
@@ -226,59 +315,81 @@ sub createrepo
     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";
+    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";
-           }
-       }
+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