Updates to QA scrdiff script to keep package configure macros in sync.
[xfstests-dev.git] / tools / srcdiff
1 #!/usr/bin/perl -w
2 use strict;
3
4 # srcdiff is used to compare current user level code with the current
5 # kernel code and advise of any differences between files which are
6 # sharing some or all of their content.
7
8 # There are two classes of sharing which we will check - header files
9 # in the include directory, which must be exactly the same (use diff)
10 # and source files which contain routines which must be exactly the
11 # same (but the userland file is always a subset of the kernel file,
12 # and hence a more flexible mechanism to "diff" is required).
13
14 # NB: to cross check that srcdiff is finding all the functions in the
15 #     user source file, providing you have "mkproto" installed, you
16 #     can "cd cmd/xfsprogs/libxfs" and cut&paste this in a bourne shell:
17 #     $ for file in xfs_*.c; do
18 #     > mkproto -nps < $file | perl -ne '
19 #     > END { print "    $count\t- " }
20 #     > s/^.* (xfs\w+|\*xfs\w+|xlog\w+|\*xlog\w+) \(.*/\1/ && { $count++ }'
21 #     > echo $file
22 #     > done
23 # (compare this to "srcdiff | fgrep Total:")
24
25
26 die "WORKAREA not set" unless defined $ENV{'WORKAREA'};
27 chdir $ENV{'WORKAREA'};
28 my $xdiff = $ENV{'XDIFF'};
29 my $quiet=0;
30 my $usage=0;
31
32 foreach (@ARGV) {
33         if (/^-q$/) {
34                 $quiet++;
35         } else {
36                 print STDERR "Illegal option $_\n";
37                 $usage++;
38         }
39 }
40
41 if ($usage) {
42     print STDERR "Usage: $0 [-q]\n";
43     exit 1;
44 }
45
46 my @pkglist = qw( attr acl dmapi xfsdump xfsprogs );
47 my @difflist = qw(
48         xfs_ag.h  xfs_alloc.h  xfs_alloc_btree.h xfs_arch.h
49         xfs_attr_leaf.h  xfs_attr_sf.h  xfs_bit.h  xfs_bmap.h
50         xfs_bmap_btree.h  xfs_btree.h  xfs_buf_item.h
51         xfs_da_btree.h  xfs_dfrag.h  xfs_dinode.h  xfs_dir.h
52         xfs_dir2.h  xfs_dir2_block.h  xfs_dir2_data.h
53         xfs_dir2_leaf.h  xfs_dir2_node.h  xfs_dir2_sf.h
54         xfs_dir_leaf.h  xfs_dir_sf.h  xfs_extfree_item.h  xfs_ialloc.h
55         xfs_imap.h  xfs_ialloc_btree.h  xfs_inode.h  xfs_inode_item.h
56         xfs_inum.h  xfs_log.h  xfs_log_priv.h  xfs_log_recover.h
57         xfs_mount.h  xfs_quota.h  xfs_rtalloc.h
58         xfs_sb.h  xfs_trans.h  xfs_trans_space.h  xfs_types.h
59         xfs_fs.h xfs_acl.h xfs_cap.h xfs_mac.h
60 );
61
62 sub straightdiff {
63         my ( $file, $prefix1, $prefix2 ) = @_;
64
65         `diff $prefix1/$file $prefix2/$file >/dev/null 2>&1`;
66         if (!$quiet) {
67                 print sprintf("\t%-35s ... ", $file);
68                 if ($? != 0)    { print "FAILED\n"; }
69                 else            { print "ok\n"; }
70         } elsif ($? != 0) { 
71                 printf("\t%-35s ... ", $file);
72                 print "FAILED\n"; 
73         }
74 }
75
76 #
77 # xfstests directory m4 directory is a repository of all of the
78 # custom m4 macros used in the packages we look after.
79 #
80 sub m4macrodiff {
81         my ( $package ) = @_;
82
83         foreach (`ls cmd/$package/m4/*.m4`) {
84                 my $m4 = `basename $_`;
85                 chomp($m4);
86                 straightdiff $m4, "cmd/$package/m4", "cmd/xfstests/m4";
87         }
88 }
89
90 my $first = shift @pkglist;
91 foreach (@pkglist) {
92         print "\n=== Checking $_ package ===\n";
93         m4macrodiff $_;
94         straightdiff 'buildrules', "cmd/$first/include", "cmd/$_/include";
95         straightdiff 'buildmacros', "cmd/$first/include", "cmd/$_/include";
96         straightdiff 'Makefile', "cmd/$first/build", "cmd/$_/build";
97         straightdiff 'Makefile', "cmd/$first/build/rpm", "cmd/$_/build/rpm";
98         straightdiff 'Makefile', "cmd/$first/build/tar", "cmd/$_/build/tar";
99 }
100 print "\n=== Checking headers ===\n";
101 foreach (@difflist) {
102         straightdiff $_, 'cmd/xfsprogs/include', 'linux/fs/xfs';
103 }
104 straightdiff 'dmapi_kern.h', 'cmd/dmapi/include', 'linux/fs/xfs/dmapi';
105 straightdiff 'dmapi.h', 'cmd/dmapi/include', 'linux/fs/xfs/dmapi';
106
107
108 # setstate
109 # Implements a tri-state FSA, see comments for state transitions
110 #  (knows about the way the XFS kernel code is written, & makes
111 #   some assumptions so as to not need to parse generic C code).
112 # Accepts one line at a time from a source file, picking out the
113 #   function bodies so they can be subsequently compared.
114
115
116 my $line;       # line number in current source file
117 my $state;      # current FSA state
118 my $funcbody;   # current function body (contents)
119
120 sub setstate {
121         my ( $newline ) = @_;
122         $line++;
123
124         # - state 0:
125         #       if line looks like start of a function, transition to 1
126         #               & squirrel line away as line 1 of current function
127         if ($state == 0) {
128                 if ($newline =~ m/^[xfs|xlog]/) {
129                         $state = 1;
130                         $funcbody = $newline;
131                 }
132         }
133
134         # - state 1:
135         #       if line looks like start of a function, stay here
136         #               & squirrel line away as line 1 of current function
137         #       otherwise if line isn't start of function body,
138         #               squirrel line away as next line of current function
139         #               (args/..., but not sure this is a real function yet)
140         #       otherwise (start of function)
141         #               squirrel line away as next line of current function
142         #               transition to state 2
143         elsif ($state == 1) {
144                 if ($newline =~ m/^[xfs|xlog]/) {
145                         $funcbody = $newline;
146                 }
147                 elsif ($newline =~ m/^\{/) {
148                         $state = 2;
149                         $funcbody .= $newline;
150                 }
151         }
152
153         # - state 2:
154         #       if line looks like end of function body,
155         #               squirrel line away as last line of current function
156         #               tell someone we have a complete function ready
157         #               transition to state 0
158         #       otherwise
159         #               squirrel line away as next line of current function
160         elsif ($state == 2) {
161                 $funcbody .= $newline;
162                 if ($newline =~ m/^\}/) {
163                         $state = 0;
164                         return $funcbody;
165                 }
166         }
167
168         else {
169                 die "unknown state transition";
170         }
171         return undef;   # i.e. not at end of a function
172 }
173
174 sub listfuncs {
175         my ( $file ) = @_;
176         my @funcs;
177
178         $funcbody = '';
179         $state = $line = 0;
180
181         open(USER, "$file") || die "cannot open $file";
182         while (<USER>) {
183                 my $func = setstate($_);
184                 push @funcs, $func if (defined($func)); # store function away
185         }
186         close USER;
187         return @funcs;
188 }
189
190 sub hashfuncs {
191         my ( $file ) = @_;
192         my %funcs;
193
194         $funcbody = '';
195         $state = $line = 0;
196
197         open(KERN, "$file") || die "cannot open $file";
198         while (<KERN>) {
199                 my $func = setstate($_);
200                 if (defined($func)) {
201                         $func =~ m/^([xfs|xlog]\w+)\s*\(/;
202                         next unless defined($1);
203                         my $name = $1;
204                         if (defined($func)) {
205                                 $funcs{$name} = $func;  # store function away
206                         }
207                 }
208         }
209         close KERN;
210         return %funcs;
211 }
212
213 sub diffme {
214         my ( $sa, $sb ) = @_;
215
216         return unless defined($xdiff);
217
218         open(FILEA, "> /tmp/diff.user.$$") || die "cannot write to /tmp/diff.user.$$";
219         open(FILEB, "> /tmp/diff.kern.$$") || die "cannot write to /tmp/diff.kern.$$";
220         print FILEA $sa;
221         print FILEB $sb;
222         close FILEA;
223         close FILEB;
224         `$xdiff /tmp/diff.user.$$ /tmp/diff.kern.$$`;
225         unlink ("/tmp/diff.user.$$","/tmp/diff.kern.$$");
226 }
227
228 sub functiondiff {
229         my ( $file, $prefix1, $prefix2 ) = @_;
230         my $plural = '';
231         my $count = 0;
232         my $name;
233         my $found = 0;
234
235         print "\n=== Checking $file routines ===\n" unless ($quiet);
236
237         # iterate over user funcs, match up to kernel funcs
238         # 
239         my @user = listfuncs "$prefix1/$file";
240         my %kern = hashfuncs "$prefix2/$file";
241
242         foreach my $userfunc (@user) {
243                 
244                 $userfunc =~ m/^([xfs|xlog]\w+)\s*\(/;
245                 next unless (defined($1));
246                 $name = $1;
247                 $count++;
248
249                 if (exists($kern{$name})) {
250                         if ($userfunc ne $kern{$name}) {
251                                 print "\n=== $file routines ===\n"
252                                     if (!$found++ && $quiet);
253                                     
254                                 printf("\t%-35s ... ", $name);
255                                 print "FAILED\n";
256                                 diffme $userfunc, $kern{$name};
257                         }
258                         elsif (!$quiet) {
259                                 printf("\t%-35s ... ", $name);
260                                 print "ok\n";
261                         }
262                 }
263                 else {
264                         print "Cannot find kernel function $userfunc";
265                         print " in file $prefix2/$file\n";
266                 }
267         }
268         ($count != 1) && ( $plural = 's' );
269         print "( Total: $count routine$plural checked in $file )\n" unless ($quiet);
270 }
271
272 # cmd/xfsprogs/{libxfs,libxlog}/* fs/xfs/*
273 my @funclist = qw(
274         xfs_alloc.c  xfs_alloc_btree.c  xfs_attr_leaf.c
275         xfs_bmap.c  xfs_bmap_btree.c  xfs_btree.c  xfs_da_btree.c
276         xfs_dir.c  xfs_dir2.c  xfs_dir2_block.c  xfs_dir2_data.c
277         xfs_dir2_leaf.c  xfs_dir2_node.c  xfs_dir2_sf.c
278         xfs_dir_leaf.c  xfs_ialloc.c  xfs_ialloc_btree.c
279         xfs_inode.c  xfs_rtalloc.c  xfs_mount.c  xfs_trans.c
280 );
281
282 print "\n=== Checking libxfs code ===\n";
283 foreach (@funclist) {
284         functiondiff $_, 'cmd/xfsprogs/libxfs', 'linux/fs/xfs';
285 }
286 print "\n=== Checking libxlog code ===\n";
287 functiondiff 'xfs_log_recover.c', 'cmd/xfsprogs/libxlog', 'linux/fs/xfs';