7 my @generated_files = qw( proto.h configure config.h.in rsync.1 rsyncd.conf.5 );
9 my($no_cvs, $failures_only, $minor_updates, $prepare_source);
12 &Getopt::Long::Configure('bundling');
14 'no-cvs|n' => \$no_cvs,
15 'failures-only|f' => \$failures_only,
16 'minor-updates|u' => \$minor_updates,
17 'prepare-source|p' => \$prepare_source,
18 'auto-cmd|a=s' => sub { push(@auto_cmds, $_[1]) },
22 my $auto_regex = @auto_cmds ? qr/^(@auto_cmds)$/i : qr/^never$/;
23 my $interesting_fuzz = $minor_updates ? '\d' : '[2-9]';
26 chdir('patches') if -d 'patches';
28 if (!-f 'verify-patches') {
30 Please run this script from the root of the rsync dir or
31 from inside the patches subdir.
35 my $patches_dir = Cwd::cwd();
37 $ENV{'LC_COLLATE'} = 'C';
39 my $CONF_OPTS = '--cache-file=../config.cache';
41 my($has_dependencies, @new, @rejects);
45 system "rsync -a --del cvsdir/ workdir/" if -d 'cvsdir';
46 my $pid = readlink('lock') || 0;
47 unlink('lock') if $pid == $$;
51 open(IN, '../CVS/Root') or die $!;
55 mkdir('tmp', 0777) unless -d 'tmp';
56 chdir('tmp') or die "Unable to chdir to 'tmp'";
58 symlink($patches_dir, 'patches');
60 symlink($$, 'lock') or die "Unable to create lock file: $!\n";
62 mkdir('workdir') unless -d 'workdir';
63 open(OUT, '>exclude') or die $!;
64 print OUT join("\n", 'CVS', @generated_files), "\n";
68 print "Using CVS to update the tmp/cvsdir copy of the source.\n";
69 system qq|cvs -qd "$root" co -P -d cvsdir rsync|;
70 @_ = qw( configure configure.in config.h.in configure.in
71 rsync.1 rsync.yo rsyncd.conf.5 rsyncd.conf.yo );
73 my $gen = 'cvsdir/' . shift(@_);
74 my $src = 'cvsdir/' . shift(@_);
75 if ((-M $gen) > (-M $src)) {
81 @ARGV = glob('patches/*.diff') unless @ARGV;
84 foreach my $diff (@ARGV) {
85 next unless $diff =~ /\.diff$/;
86 next if $diff =~ /gzip-rsyncable[-_a-z]*\.diff$/;
87 $diff =~ s#^(patches|\.\.)/##;
90 open(IN, "patches/$diff") or die $!;
93 if (m#^\s+patch -p1 <patches/(\S+.diff)# && $1 ne $diff) {
95 $has_dependencies = 1;
96 print "\nApplying dependency patch $dep...\n";
97 if (system("patch -f -d cvsdir -p1 -b -Vt <patches/$dep") != 0) {
98 print "Unable to cleanly apply dependency patch -- skipping $diff\n";
99 system "rm -f cvsdir/*.rej cvsdir/*/*.rej";
103 sleep(1); # Ensure later diffs get later times.
105 if (!defined($conf_opts) && s#^\s+\./configure\s+##) {
106 chomp($conf_opts = $_);
107 $conf_opts =~ s/\s*\(.*?\)//;
111 $conf_opts = '' unless defined $conf_opts;
113 my $default = apply_patch($diff);
115 if ($prepare_source) {
116 print "\nPreparing the source...\n";
117 chdir('workdir') or die $!;
118 system "./prepare-source";
119 chdir('..') or die $!;
122 if ($default =~ s/^D,// || $default eq 'N') {
123 my $def = generate_new_patch($diff);
124 $default = 'U,N' if $default eq 'N' && $def eq 'E';
125 $default = 'N' if !$minor_updates && $default eq 'U,N';
131 print "\n----------- $diff ------------\n",
132 "\nFix rejects, Diff create, Edit both diffs, Update patch,\n",
133 "Apply patch again, !(CMD), Build rsync, Next, Quit: [$default] ";
135 if ($first_time && $default =~ /$auto_regex/) {
136 print $default, "\n";
140 if ($input =~ s/^(-a|--auto-cmd=?)\s*//) {
141 push(@auto_cmds, $input eq '' ? $default : $input);
143 $auto_regex = qr/^(@auto_cmds)$/i;
147 $ans = $input if $input ne '';
150 while ($ans =~ s/^\s*(!|\w)((?<!!)[^;,]*|[^;]*)[;,]?//) {
153 $cmd = $2 || $ENV{'SHELL'};
154 chdir('workdir') or die $!;
156 chdir('..') or die $!;
161 $default = apply_patch($diff);
165 chdir('workdir') or die $!;
166 my $cmd = "./prepare-source && ./configure $CONF_OPTS $conf_opts && make";
167 print "Running: $cmd\n";
169 chdir('..') or die $!;
170 $default = '!make test';
174 $default = generate_new_patch($diff);
178 chdir('workdir') or die $!;
179 system "vim -d ../patches/$diff ../new.patch";
180 chdir('..') or die $!;
185 chdir('workdir') or die $!;
186 system "vim @rejects";
187 chdir('..') or die $!;
198 system "cp -p new.patch patches/$diff";
199 print "\nUpdated $diff from new.patch\n";
217 system "rsync -a --del --exclude='*~' cvsdir/ workdir/";
218 print "\nApplying patch $diff...\n";
220 my($saw_offset, $saw_fuzz);
221 open(IN, "patch -f -d workdir -p1 --no-backup-if-mismatch <patches/$diff |") or die $!;
225 if (s/^patching file //) {
226 push(@new, $_) unless -f "cvsdir/$_";
227 } elsif (s/.* saving rejects to file //) {
229 } elsif (/No file to patch\.\s+Skipping patch/) {
230 push(@rejects, 'skipped.file');
231 } elsif (/^Hunk #\d+ succeeded at \d+( with fuzz $interesting_fuzz)?/o) {
232 $saw_fuzz ||= defined $1;
237 return 'F,D,E' if @rejects;
238 return 'D,E' if $saw_fuzz && !$failures_only;
239 return 'D,U,N' if $saw_offset && !$failures_only;
246 open(IN, '-|', $cmd) or die $!;
248 next if /^(diff -|Index: |Only in )/;
249 s#^\Q--- cvsdir/\E([^\t]+).*#--- old/$1#;
250 s#^\Q+++ workdir/\E([^\t]+).*#+++ new/$1#;
256 sub generate_new_patch
261 system "touch -r workdir/$_ cvsdir/$_";
263 open(IN, "patches/$diff") or die $!;
264 open(OUT, '>new.patch') or die $!;
270 &filter_diff('diff --exclude-from=exclude -dupr cvsdir workdir');
271 if ($prepare_source) {
272 # These are not included in the diff above so that patch will give
273 # generated files a later timestamp than the source files.
274 foreach my $fn (@generated_files) {
275 &filter_diff("diff -dupW128 cvsdir/$fn workdir");
282 print "\nDiffing... ";
283 if (system("diff patches/$diff new.patch >/dev/null") == 0) {
284 print "new patch is identical to old.\n";
287 print "New patch DIFFERS from old.\n";
293 return unless $has_dependencies;
294 $has_dependencies = 0;
296 chdir('cvsdir') or die $!;
297 foreach (glob('*.~[1-9]~'), glob('*/*.~[1-9]~')) {
299 ($fn = $_) =~ s/\.~1~$//;
309 chdir('..') or die $!;
315 Usage: $0 [OPTS] [DIFF-FILE...]
316 -a, --auto-cmd=REGEX If default_cmd =~ /^(REGEX)\$/, enter it automatically
317 -f, --failures-only Suggest skipping patches that don't have failing hunks
318 -n, --no-cvs Don't update tmp/cvsdir at the start of the run
319 -p, --prepare-source Run ./prepare-source and include generated files in diff
320 -u, --minor-updates Suggest 'U' for even minor changes in the diff