blob: 7fd83cb8d7566dd8fd1f1a00bc7f8c8358dffb02 [file] [log] [blame]
[email protected]2ec654a2012-01-10 17:47:001#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]03fcdb902011-06-27 18:03:053# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Raul Tambrea4895d82019-04-08 19:18:266from __future__ import print_function
7
[email protected]03fcdb902011-06-27 18:03:058import glob
9import os
10import subprocess
11import sys
12
13from idl_option import GetOption, Option, ParseOptions
[email protected]5eef2882011-07-19 00:08:5414from idl_outfile import IDLOutFile
[email protected]03fcdb902011-06-27 18:03:0515#
16# IDLDiff
17#
18# IDLDiff is a tool for comparing sets of IDL generated header files
19# with the standard checked in headers. It does this by capturing the
20# output of the standard diff tool, parsing it into separate changes, then
21# ignoring changes that are know to be safe, such as adding or removing
22# blank lines, etc...
23#
24
25Option('gen', 'IDL generated files', default='hdir')
26Option('src', 'Original ".h" files', default='../c')
[email protected]5eef2882011-07-19 00:08:5427Option('halt', 'Stop if a difference is found')
28Option('diff', 'Directory holding acceptable diffs', default='diff')
29Option('ok', 'Write out the diff file.')
[email protected]03fcdb902011-06-27 18:03:0530# Change
31#
32# A Change object contains the previous lines, new news and change type.
33#
34class Change(object):
[email protected]5eef2882011-07-19 00:08:5435 def __init__(self, mode, was, now):
36 self.mode = mode
37 self.was = was
38 self.now = now
[email protected]03fcdb902011-06-27 18:03:0539
40 def Dump(self):
41 if not self.was:
Raul Tambrea4895d82019-04-08 19:18:2642 print('Adding %s' % self.mode)
[email protected]03fcdb902011-06-27 18:03:0543 elif not self.now:
Raul Tambrea4895d82019-04-08 19:18:2644 print('Missing %s' % self.mode)
[email protected]03fcdb902011-06-27 18:03:0545 else:
Raul Tambrea4895d82019-04-08 19:18:2646 print('Modifying %s' % self.mode)
[email protected]03fcdb902011-06-27 18:03:0547
48 for line in self.was:
Raul Tambrea4895d82019-04-08 19:18:2649 print('src: >>%s<<' % line)
[email protected]03fcdb902011-06-27 18:03:0550 for line in self.now:
Raul Tambrea4895d82019-04-08 19:18:2651 print('gen: >>%s<<' % line)
[email protected]03fcdb902011-06-27 18:03:0552 print
53
54#
55# IsCopyright
56#
57# Return True if this change is only a one line change in the copyright notice
58# such as non-matching years.
59#
60def IsCopyright(change):
[email protected]5eef2882011-07-19 00:08:5461 if len(change.now) != 1 or len(change.was) != 1: return False
62 if 'Copyright (c)' not in change.now[0]: return False
63 if 'Copyright (c)' not in change.was[0]: return False
64 return True
65
66#
67# IsBlankComment
68#
69# Return True if this change only removes a blank line from a comment
70#
71def IsBlankComment(change):
72 if change.now: return False
73 if len(change.was) != 1: return False
74 if change.was[0].strip() != '*': return False
[email protected]03fcdb902011-06-27 18:03:0575 return True
76
77#
78# IsBlank
79#
80# Return True if this change only adds or removes blank lines
81#
82def IsBlank(change):
[email protected]5eef2882011-07-19 00:08:5483 for line in change.now:
[email protected]03fcdb902011-06-27 18:03:0584 if line: return False
[email protected]5eef2882011-07-19 00:08:5485 for line in change.was:
[email protected]03fcdb902011-06-27 18:03:0586 if line: return False
87 return True
88
89
90#
[email protected]5eef2882011-07-19 00:08:5491# IsCppComment
92#
93# Return True if this change only going from C++ to C style
94#
95def IsToCppComment(change):
96 if not len(change.now) or len(change.now) != len(change.was):
97 return False
98 for index in range(len(change.now)):
99 was = change.was[index].strip()
100 if was[:2] != '//':
101 return False
102 was = was[2:].strip()
103 now = change.now[index].strip()
104 if now[:2] != '/*':
105 return False
106 now = now[2:-2].strip()
107 if now != was:
108 return False
109 return True
110
111
112 return True
113
114def IsMergeComment(change):
115 if len(change.was) != 1: return False
116 if change.was[0].strip() != '*': return False
117 for line in change.now:
118 stripped = line.strip()
119 if stripped != '*' and stripped[:2] != '/*' and stripped[-2:] != '*/':
120 return False
121 return True
122#
[email protected]03fcdb902011-06-27 18:03:05123# IsSpacing
124#
125# Return True if this change is only different in the way 'words' are spaced
126# such as in an enum:
127# ENUM_XXX = 1,
128# ENUM_XYY_Y = 2,
129# vs
130# ENUM_XXX = 1,
131# ENUM_XYY_Y = 2,
132#
133def IsSpacing(change):
[email protected]5eef2882011-07-19 00:08:54134 if len(change.now) != len(change.was): return False
135 for i in range(len(change.now)):
[email protected]03fcdb902011-06-27 18:03:05136 # Also ignore right side comments
[email protected]5eef2882011-07-19 00:08:54137 line = change.was[i]
[email protected]03fcdb902011-06-27 18:03:05138 offs = line.find('//')
139 if offs == -1:
140 offs = line.find('/*')
141 if offs >-1:
142 line = line[:offs-1]
143
[email protected]5eef2882011-07-19 00:08:54144 words1 = change.now[i].split()
[email protected]03fcdb902011-06-27 18:03:05145 words2 = line.split()
146 if words1 != words2: return False
147 return True
148
149#
150# IsInclude
151#
152# Return True if change has extra includes
153#
154def IsInclude(change):
[email protected]5eef2882011-07-19 00:08:54155 for line in change.was:
156 if line.strip().find('struct'): return False
157 for line in change.now:
[email protected]03fcdb902011-06-27 18:03:05158 if line and '#include' not in line: return False
159 return True
160
161#
162# IsCppComment
163#
164# Return True if the change is only missing C++ comments
165#
166def IsCppComment(change):
[email protected]5eef2882011-07-19 00:08:54167 if len(change.now): return False
168 for line in change.was:
[email protected]03fcdb902011-06-27 18:03:05169 line = line.strip()
170 if line[:2] != '//': return False
171 return True
172#
173# ValidChange
174#
175# Return True if none of the changes does not patch an above "bogus" change.
176#
177def ValidChange(change):
[email protected]5eef2882011-07-19 00:08:54178 if IsToCppComment(change): return False
[email protected]03fcdb902011-06-27 18:03:05179 if IsCopyright(change): return False
[email protected]5eef2882011-07-19 00:08:54180 if IsBlankComment(change): return False
181 if IsMergeComment(change): return False
[email protected]03fcdb902011-06-27 18:03:05182 if IsBlank(change): return False
183 if IsSpacing(change): return False
184 if IsInclude(change): return False
185 if IsCppComment(change): return False
186 return True
187
188
[email protected]5eef2882011-07-19 00:08:54189#
190# Swapped
191#
192# Check if the combination of last + next change signals they are both
193# invalid such as swap of line around an invalid block.
194#
195def Swapped(last, next):
196 if not last.now and not next.was and len(last.was) == len(next.now):
197 cnt = len(last.was)
198 for i in range(cnt):
199 match = True
200 for j in range(cnt):
201 if last.was[j] != next.now[(i + j) % cnt]:
202 match = False
203 break;
204 if match: return True
205 if not last.was and not next.now and len(last.now) == len(next.was):
206 cnt = len(last.now)
207 for i in range(cnt):
208 match = True
209 for j in range(cnt):
210 if last.now[i] != next.was[(i + j) % cnt]:
211 match = False
212 break;
213 if match: return True
214 return False
[email protected]03fcdb902011-06-27 18:03:05215
[email protected]5eef2882011-07-19 00:08:54216
217def FilterLinesIn(output):
218 was = []
219 now = []
220 filter = []
221 for index in range(len(output)):
222 filter.append(False)
223 line = output[index]
224 if len(line) < 2: continue
225 if line[0] == '<':
226 if line[2:].strip() == '': continue
227 was.append((index, line[2:]))
228 elif line[0] == '>':
229 if line[2:].strip() == '': continue
230 now.append((index, line[2:]))
231 for windex, wline in was:
232 for nindex, nline in now:
233 if filter[nindex]: continue
234 if filter[windex]: continue
235 if wline == nline:
236 filter[nindex] = True
237 filter[windex] = True
238 if GetOption('verbose'):
Raul Tambrea4895d82019-04-08 19:18:26239 print("Found %d, %d >>%s<<" % (windex + 1, nindex + 1, wline))
[email protected]5eef2882011-07-19 00:08:54240 out = []
241 for index in range(len(output)):
242 if not filter[index]:
243 out.append(output[index])
244
245 return out
[email protected]03fcdb902011-06-27 18:03:05246#
247# GetChanges
248#
249# Parse the output into discrete change blocks.
250#
251def GetChanges(output):
[email protected]5eef2882011-07-19 00:08:54252 # Split on lines, adding an END marker to simply add logic
[email protected]03fcdb902011-06-27 18:03:05253 lines = output.split('\n')
[email protected]5eef2882011-07-19 00:08:54254 lines = FilterLinesIn(lines)
255 lines.append('END')
256
[email protected]03fcdb902011-06-27 18:03:05257 changes = []
[email protected]5eef2882011-07-19 00:08:54258 was = []
259 now = []
260 mode = ''
261 last = None
262
[email protected]03fcdb902011-06-27 18:03:05263 for line in lines:
Raul Tambrea4895d82019-04-08 19:18:26264 #print("LINE=%s" % line)
[email protected]03fcdb902011-06-27 18:03:05265 if not line: continue
[email protected]5eef2882011-07-19 00:08:54266
[email protected]03fcdb902011-06-27 18:03:05267 elif line[0] == '<':
[email protected]5eef2882011-07-19 00:08:54268 if line[2:].strip() == '': continue
269 # Ignore prototypes
270 if len(line) > 10:
271 words = line[2:].split()
272 if len(words) == 2 and words[1][-1] == ';':
273 if words[0] == 'struct' or words[0] == 'union':
274 continue
275 was.append(line[2:])
[email protected]03fcdb902011-06-27 18:03:05276 elif line[0] == '>':
[email protected]5eef2882011-07-19 00:08:54277 if line[2:].strip() == '': continue
278 if line[2:10] == '#include': continue
279 now.append(line[2:])
[email protected]03fcdb902011-06-27 18:03:05280 elif line[0] == '-':
281 continue
282 else:
[email protected]5eef2882011-07-19 00:08:54283 change = Change(line, was, now)
284 was = []
285 now = []
286 if ValidChange(change):
Raul Tambrea4895d82019-04-08 19:18:26287 changes.append(change)
[email protected]5eef2882011-07-19 00:08:54288 if line == 'END':
289 break
290
291 return FilterChanges(changes)
292
293def FilterChanges(changes):
294 if len(changes) < 2: return changes
295 out = []
296 filter = [False for change in changes]
297 for cur in range(len(changes)):
298 for cmp in range(cur+1, len(changes)):
299 if filter[cmp]:
300 continue
301 if Swapped(changes[cur], changes[cmp]):
302 filter[cur] = True
303 filter[cmp] = True
304 for cur in range(len(changes)):
305 if filter[cur]: continue
306 out.append(changes[cur])
307 return out
[email protected]03fcdb902011-06-27 18:03:05308
309def Main(args):
310 filenames = ParseOptions(args)
311 if not filenames:
312 gendir = os.path.join(GetOption('gen'), '*.h')
[email protected]5eef2882011-07-19 00:08:54313 filenames = sorted(glob.glob(gendir))
314 srcdir = os.path.join(GetOption('src'), '*.h')
315 srcs = sorted(glob.glob(srcdir))
316 for name in srcs:
317 name = os.path.split(name)[1]
318 name = os.path.join(GetOption('gen'), name)
319 if name not in filenames:
Raul Tambrea4895d82019-04-08 19:18:26320 print('Missing: %s' % name)
[email protected]03fcdb902011-06-27 18:03:05321
322 for filename in filenames:
323 gen = filename
324 filename = filename[len(GetOption('gen')) + 1:]
325 src = os.path.join(GetOption('src'), filename)
[email protected]5eef2882011-07-19 00:08:54326 diff = os.path.join(GetOption('diff'), filename)
[email protected]03fcdb902011-06-27 18:03:05327 p = subprocess.Popen(['diff', src, gen], stdout=subprocess.PIPE)
328 output, errors = p.communicate()
329
[email protected]5eef2882011-07-19 00:08:54330 try:
331 input = open(diff, 'rt').read()
332 except:
333 input = ''
334
335 if input != output:
336 changes = GetChanges(output)
337 else:
338 changes = []
339
[email protected]03fcdb902011-06-27 18:03:05340 if changes:
Raul Tambrea4895d82019-04-08 19:18:26341 print("\n\nDelta between:\n src=%s\n gen=%s\n" % (src, gen))
[email protected]5eef2882011-07-19 00:08:54342 for change in changes:
[email protected]03fcdb902011-06-27 18:03:05343 change.Dump()
Raul Tambrea4895d82019-04-08 19:18:26344 print('Done with %s\n\n' % src)
[email protected]5eef2882011-07-19 00:08:54345 if GetOption('ok'):
346 open(diff, 'wt').write(output)
347 if GetOption('halt'):
348 return 1
[email protected]03fcdb902011-06-27 18:03:05349 else:
Raul Tambrea4895d82019-04-08 19:18:26350 print("\nSAME:\n src=%s\n gen=%s" % (src, gen))
351 if input:
352 print(' ** Matched expected diff. **')
353 print('\n')
[email protected]03fcdb902011-06-27 18:03:05354
[email protected]2ec654a2012-01-10 17:47:00355
[email protected]03fcdb902011-06-27 18:03:05356if __name__ == '__main__':
357 sys.exit(Main(sys.argv[1:]))