rebuildperl: Fix a typo
[Fedora-Rebuild.git] / bin / comparebuildroots
blob46c44b7f042c2c7c57149bcb6ba11d67177155c1
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
5 use Proc::SyncExec;
6 use Fedora::Rebuild::Package::StateLock;
7 use RPM::VersionCompare;
8 use Fedora::Rebuild::Scheduler;
9 use Carp;
11 sub usage {
12 print<<EOM;
13 Usage: comparebuildroots THREADS OLD_TAG NEW_TAG [LIST]
15 Loads list of source package base names from file LIST or standard input,
16 queries koji for latest builds with OLD_TAG and NEW_TAG, compares their
17 versions and reports differences. The query will run in parallel
18 number of THREADS.
20 Output format is a package per line. First column is package name, second
21 column is version from OLD_TAG, and third column is version from NEW_TAG.
22 Columns are separated by tabulator. If a package is missing in given tag,
23 empty string is printed.
25 Package name is preceeded by a character signaling difference between tags.
26 A space stands for equaled builds, plus for newer build in NEW_TAG, minus for
27 newer build in OLD_TAG.
29 If an error occures, the script exits immediately with non-zero return code.
30 EOM
33 # Print message to error output
34 sub log_error {
35 print STDERR @_;
39 # Run command while appending stderr and stdout to log and stdout to refered
40 # output argument. In case of empty command output fill empty string;
41 # Blocks. If workdir is nonempty string, switch into it befere execution
42 # (and opening the log).
43 # Return true if command succeeds.
44 sub dooutput {
45 my ($output, @command) = @_;
47 my ($parent, $child);
48 if (!pipe $child, $parent) {
49 log_error("Could not get connected pipes for command " .
50 Fedora::Rebuild::Package::StateLock::format_command(@command) .
51 ": $!\n");
52 return 0;
55 my $redirect = sub {
56 close $child and
57 open(STDOUT, '>&', fileno $parent) and
58 close $parent and
60 return 1;
62 my $pid = Proc::SyncExec::sync_exec($redirect, @command);
64 my $errno = $!;
65 close $parent;
66 $! = $errno;
68 if (!defined $pid) {
69 log_error("Could not execute " .
70 Fedora::Rebuild::Package::StateLock::format_command(@command) .
71 ": $!\n");
72 return 0;
75 for ($$output = ''; local $_ = <$child>;) {
76 $$output .= $_;
79 if ($pid != waitpid($pid, 0) || $?) {
80 log_error("Command " .
81 Fedora::Rebuild::Package::StateLock::format_command(@command) .
82 " failed: " .
83 Fedora::Rebuild::Package::StateLock::child_error_as_string . "\n");
84 return 0;
87 return 1;
91 # Get latest build in a tag as version-release string.
92 # Return build string in case of succes, empty string if package has not yet
93 # been built, undef in case of errror;
94 sub get_latest_build {
95 my ($tag, $package) = @_;
96 my $build = '';
97 if (!dooutput(\$build, 'koji', 'latest-pkg', '--quiet', $tag, $package)) {
98 return undef;
101 if (!defined $build || $build eq '') {
102 return '';
105 # Get first word
106 if (! ($build =~ /^([\S]+)/)) {
107 return undef;
109 $build = $1;
111 # Remove package base name
112 if (! ($build =~ /^.*-([^-]+-[^-]+)$/)) {
113 return undef;
116 return $1;
120 # Compare version-release string of new and old build.
121 # Returns 0 if equaled, 1 if $new is bigger, -1 if $new od older, undef in
122 # case of error.
123 sub compare {
124 my ($new, $old) = @_;
126 if ($new eq '') {
127 if ($old eq '') {
128 return 0;
130 return -1;
133 if ($old eq '') {
134 return 1;
137 return RPM::VersionCompare::labelCompare($new, $old);
140 # Compare a package in the buildroots
141 sub compare_package {
142 my ($package, $old_tag, $new_tag) = @_;
144 my $old_build = get_latest_build($old_tag, $package);
145 my $new_build = get_latest_build($new_tag, $package);
147 if (!defined $old_build || !defined $new_build) {
148 log_error("Could not retrieve latest builds of `" . $package . "'.\n");
149 exit 1;
152 my $order = compare($new_build, $old_build);
153 if (!defined $order) {
154 log_error("Could not compare `" . $old_build. "' and `" .
155 $new_build . "'.\n");
156 exit 1;
159 my $diff = ' ';
160 if ($order < 0) {
161 $diff = '-';
162 } elsif ($order > 0) {
163 $diff = '+';
165 print $diff . "$package\t$old_build\t$new_build\n";
167 return 1;
170 # Parse arguments
171 if ($#ARGV < 2) {
172 usage();
173 exit 1;
175 my $threads = shift @ARGV;
176 my $old_tag = shift @ARGV;
177 my $new_tag = shift @ARGV;
178 my @packages;
181 # Load list of packages
182 while (<>) {
183 chomp;
184 push @packages, $_;
188 # Check each package
189 my $scheduler = Fedora::Rebuild::Scheduler->new(
190 limit => $threads,
191 #name => 'Comparing build roots',
192 #total => $#packages
194 my %jobs= ();
195 my $i = 0;
197 foreach my $package (@packages) {
198 my $job = $scheduler->schedule(\&compare_package, $package,
199 $old_tag, $new_tag);
200 if (! defined $job) { next; }
201 $jobs{$job} = $package;
202 my %finished = $scheduler->finish(++$i < @packages);
204 while (my ($job, $status) = each %finished) {
205 my $package = $jobs{$job};
206 if (!$$status[0]) {
207 log_error "Could check `" . $package->name . "' package.\n";
208 log_error "Waiting for finishing scheduled jobs...\n";
209 $scheduler->finish(1);
210 log_error "All jobs have finished.\n";
211 croak "Could check all packages.\n";
216 exit 0;