A Discrete-Event Network Simulator
API
check-style.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
3import os
4import subprocess
5import tempfile
6import sys
7import filecmp
8import optparse
9import shutil
10import difflib
11import re
12
14 files = os.popen('git diff --name-only')
15 process = subprocess.Popen(["git","rev-parse","--show-toplevel"],
16 stdout = subprocess.PIPE,
17 stderr = subprocess.PIPE)
18 root_dir, _ = process.communicate()
19 if isinstance(root_dir, bytes):
20 root_dir=root_dir.decode("utf-8")
21 files_changed = [item.strip() for item in files.readlines()]
22 files_changed = [item for item in files_changed if item.endswith('.h') or item.endswith('.cc')]
23 return [root_dir[: -1] + "/" + filename.strip () for filename in files_changed]
24
25def copy_file(filename):
26 _, pathname = tempfile.mkstemp()
27 with open(filename, 'r') as src, open(pathname, 'w') as dst:
28 for line in src:
29 dst.write(line)
30 return pathname
31
32# generate a temporary configuration file
34 level2 = """
35nl_if_brace=Add
36nl_brace_else=Add
37nl_elseif_brace=Add
38nl_else_brace=Add
39nl_while_brace=Add
40nl_do_brace=Add
41nl_for_brace=Add
42nl_brace_while=Add
43nl_switch_brace=Add
44nl_after_case=True
45nl_namespace_brace=ignore
46nl_after_brace_open=True
47nl_class_leave_one_liners=False
48nl_enum_leave_one_liners=False
49nl_func_leave_one_liners=False
50nl_if_leave_one_liners=False
51nl_class_colon=Ignore
52nl_before_access_spec=2
53nl_after_access_spec=0
54indent_access_spec=-indent_columns
55nl_after_semicolon=True
56pos_class_colon=Lead
57pos_class_comma=Trail
58indent_constr_colon=true
59pos_bool=Lead
60nl_class_init_args=Add
61nl_template_class=Add
62nl_class_brace=Add
63# does not work very well
64nl_func_type_name=Ignore
65nl_func_scope_name=Ignore
66nl_func_type_name_class=Ignore
67nl_func_proto_type_name=Ignore
68# function\\n(
69nl_func_paren=Remove
70nl_fdef_brace=Add
71nl_struct_brace=Add
72nl_enum_brace=Add
73nl_union_brace=Add
74mod_full_brace_do=Add
75mod_full_brace_for=Add
76mod_full_brace_if=Add
77mod_full_brace_while=Add
78mod_full_brace_for=Add
79mod_remove_extra_semicolon=True
80# max code width
81#code_width=128
82#ls_for_split_full=True
83#ls_func_split_full=True
84nl_cpp_lambda_leave_one_liners=True
85"""
86 level1 = """
87# extra spaces here and there
88sp_brace_typedef=Add
89sp_enum_assign=Add
90sp_before_sparen=Add
91sp_after_semi_for=Add
92sp_arith=Add
93sp_assign=Add
94sp_compare=Add
95sp_func_class_paren=Add
96sp_after_type=Add
97sp_type_func=Add
98sp_angle_paren=Add
99"""
100 level0 = """
101sp_func_proto_paren=Add
102sp_func_def_paren=Add
103sp_func_call_paren=Add
104sp_after_semi_for=Ignore
105sp_before_sparen=Ignore
106sp_before_ellipsis=Remove
107sp_type_func=Ignore
108sp_after_type=Ignore
109nl_class_leave_one_liners=True
110nl_enum_leave_one_liners=True
111nl_func_leave_one_liners=True
112nl_assign_leave_one_liners=True
113nl_collapse_empty_body=True
114nl_getset_leave_one_liners=True
115nl_if_leave_one_liners=True
116nl_fdef_brace=Ignore
117# finally, indentation configuration
118indent_with_tabs=0
119indent_namespace=false
120indent_columns=2
121indent_brace=2
122indent_case_brace=indent_columns
123indent_class=true
124indent_class_colon=True
125indent_switch_case=indent_columns
126# alignment
127indent_align_assign=False
128align_left_shift=True
129# comment reformating disabled
130cmt_reflow_mode=1 # do not touch comments at all
131cmt_indent_multi=False # really, do not touch them
132disable_processing_cmt= " *NS_CHECK_STYLE_OFF*"
133enable_processing_cmt= " *NS_CHECK_STYLE_ON*"
134"""
135 _, pathname = tempfile.mkstemp()
136 with open(pathname, 'w') as dst:
137 dst.write(level0)
138 if level >= 1:
139 dst.write(level1)
140 if level >= 2:
141 dst.write(level2)
142 return pathname
143
144
146
152 SRC = 1
153
155 DST = 2
156
158 BOTH = 3
159 def __init__(self):
160 """! Initializer
161 @param self The current class
162 """
163 self.__type = 0
164 self.__line = ''
165 def set_src(self,line):
166 """! Set source
167 @param self The current class
168 @param line source line
169 @return none
170 """
171 self.__type = self.SRC
172 self.__line = line
173 def set_dst(self,line):
174 """! Set destination
175 @param self The current class
176 @param line destination line
177 @return none
178 """
179 self.__type = self.DST
180 self.__line = line
181 def set_both(self,line):
182 """! Set both
183 @param self The current class
184 @param line
185 @return none
186 """
187 self.__type = self.BOTH
188 self.__line = line
189 def append_to_line(self, s):
190 """! Append to line
191 @param self The current class
192 @param s line to append
193 @return none
194 """
195 self.__line = self.__line + s
196 def line(self):
197 """! Get line
198 @param self The current class
199 @return line
200 """
201 return self.__line
202 def is_src(self):
203 """! Is source
204 @param self The current class
205 @return true if type is source
206 """
207 return self.__type == self.SRC or self.__type == self.BOTH
208 def is_dst(self):
209 """! Is destination
210 @param self The current class
211 @return true if type is destination
212 """
213 return self.__type == self.DST or self.__type == self.BOTH
214 def write(self, f):
215 """! Write to file
216 @param self The current class
217 @param f file
218 @return exception if invalid type
219 """
220 if self.__type == self.SRC:
221 f.write('-%s\n' % self.__line)
222 elif self.__type == self.DST:
223 f.write('+%s\n' % self.__line)
224 elif self.__type == self.BOTH:
225 f.write(' %s\n' % self.__line)
226 else:
227 raise Exception('invalid patch')
228
229
231
241 def __init__(self, src_pos, dst_pos):
242 """! Initializer
243 @param self: this object
244 @param src_pos: source position
245 @param dst_pos: destination position
246 """
247 self.__lines = []
248 self.__src_pos = int(src_pos)
249 self.__dst_pos = int(dst_pos)
250 def src_start(self):
251 """! Source start function
252 @param self this object
253 @return source position
254 """
255 return self.__src_pos
256 def add_line(self,line):
257 """! Add line function
258 @param self The current class
259 @param line line to add
260 @return none
261 """
262 self.__lines.append(line)
263 def src(self):
264 """! Get source lines
265 @param self The current class
266 @return the source lines
267 """
268 src = []
269 for line in self.__lines:
270 if line.is_src():
271 src.append(line)
272 return src
273 def dst(self):
274 """! Get destination lines
275 @param self The current class
276 @return the destination lines
277 """
278 dst = []
279 for line in self.__lines:
280 if line.is_dst():
281 dst.append(line)
282 return dst
283 def src_len(self):
284 """! Get number of source lines
285 @param self The current class
286 @return number of source lines
287 """
288 return len(self.src())
289 def dst_len(self):
290 """! Get number of destinaton lines
291 @param self The current class
292 @return number of destination lines
293 """
294 return len(self.dst())
295 def write(self,f):
296 """! Write lines to file
297 @param self The current class
298 @param f: file to write to
299 @return none
300 """
301 f.write('@@ -%d,%d +%d,%d @@\n' % (self.__src_pos, self.src_len(),
302 self.__dst_pos, self.dst_len()))
303 for line in self.__lines:
304 line.write(f)
305
306
307class Patch:
308
314 def __init__(self):
315 """! Initializer
316 @param self The current class
317 """
318 self.__src = ''
319 self.__dst = ''
320 self.__chunks = []
321 def add_chunk(self, chunk):
322 """! Add chunk
323 @param self this object
324 @param chunk chunk
325 @return none
326 """
327 self.__chunks.append(chunk)
328 def chunks(self):
329 """! Get the chunks
330 @param self The current class
331 @return the chunks
332 """
333 return self.__chunks
334 def set_src(self,src):
335 """! Set source
336 @param self this object
337 @param src source
338 @return none
339 """
340 self.__src = src
341 def set_dst(self,dst):
342 """! Set destination
343 @param self this object
344 @param dst destintion
345 @return none
346 """
347 self.__dst = dst
348 def apply(self,filename):
349 """! Apply function
350 @param self The current class
351 @param filename file name
352 @return none
353 """
354 # XXX: not implemented
355 return
356 def write(self,f):
357 """! Write to file
358 @param self The current class
359 @param f the file
360 @return none
361 """
362 f.write('--- %s\n' % self.__src )
363 f.write('+++ %s\n' % self.__dst )
364 for chunk in self.__chunks:
365 chunk.write(f)
366
367def parse_patchset(generator):
368 src_file = re.compile('^--- (.*)$')
369 dst_file = re.compile('^\+\+\+ (.*)$')
370 chunk_start = re.compile('^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@')
371 src = re.compile('^-(.*)$')
372 dst = re.compile('^\+(.*)$')
373 both = re.compile('^ (.*)$')
374 patchset = []
375 current_patch = None
376 for line in generator:
377 m = src_file.search(line)
378 if m is not None:
379 current_patch = Patch()
380 patchset.append(current_patch)
381 current_patch.set_src(m.group(1))
382 continue
383 m = dst_file.search(line)
384 if m is not None:
385 current_patch.set_dst(m.group(1))
386 continue
387 m = chunk_start.search(line)
388 if m is not None:
389 current_chunk = PatchChunk(m.group(1), m.group(3))
390 current_patch.add_chunk(current_chunk)
391 continue
392 m = src.search(line)
393 if m is not None:
394 l = PatchChunkLine()
395 l.set_src(m.group(1))
396 current_chunk.add_line(l)
397 continue
398 m = dst.search(line)
399 if m is not None:
400 l = PatchChunkLine()
401 l.set_dst(m.group(1))
402 current_chunk.add_line(l)
403 continue
404 m = both.search(line)
405 if m is not None:
406 l = PatchChunkLine()
407 l.set_both(m.group(1))
408 current_chunk.add_line(l)
409 continue
410 raise Exception()
411 return patchset
412
414 whitespace = re.compile('^(.*)([ \t]+)$')
415 patchset = parse_patchset(patch_generator)
416 for patch in patchset:
417 for chunk in patch.chunks():
418 src = chunk.src()
419 dst = chunk.dst()
420 try:
421 for i in range(0,len(src)):
422 s = src[i]
423 d = dst[i]
424 m = whitespace.search(s.line())
425 if m is not None and m.group(1) == d.line():
426 d.append_to_line(m.group(2))
427 except:
428 return patchset
429 return patchset
430
431
432def indent(source, debug, level):
433 output = tempfile.mkstemp()[1]
434 # apply uncrustify
435 cfg = uncrustify_config_file(level)
436 if debug:
437 sys.stderr.write('original file=' + source + '\n')
438 sys.stderr.write('uncrustify config file=' + cfg + '\n')
439 sys.stderr.write('temporary file=' + output + '\n')
440 try:
441 uncrust = subprocess.Popen(['uncrustify', '-c', cfg, '-f', source, '-o', output],
442 stdin = subprocess.PIPE,
443 stdout = subprocess.PIPE,
444 stderr = subprocess.PIPE,
445 universal_newlines = True)
446 (out, err) = uncrust.communicate('')
447 if debug:
448 sys.stderr.write(out)
449 sys.stderr.write(err)
450 except OSError:
451 raise Exception ('uncrustify not installed')
452 # generate a diff file
453 with open(source, 'r') as src, open(output, 'r') as dst:
454 diff = difflib.unified_diff(src.readlines(), dst.readlines(),
455 fromfile=source, tofile=output)
456 if debug:
457 initial_diff = tempfile.mkstemp()[1]
458 sys.stderr.write('initial diff file=' + initial_diff + '\n')
459 with open(initial_diff, 'w') as tmp:
460 tmp.writelines(diff)
461 final_diff = tempfile.mkstemp()[1]
462 if level < 3:
464 if len(patchset) != 0:
465 with open(final_diff, 'w') as dst:
466 patchset[0].write(dst)
467 else:
468 with open(final_diff, 'w') as dst:
469 dst.writelines(diff)
470
471
472 # apply diff file
473 if debug:
474 sys.stderr.write('final diff file=' + final_diff + '\n')
475 shutil.copyfile(source,output)
476 patch = subprocess.Popen(['patch', '-p1', '-i', final_diff, output],
477 stdin = subprocess.PIPE,
478 stdout = subprocess.PIPE,
479 stderr = subprocess.PIPE,
480 universal_newlines = True)
481 (out, err) = patch.communicate('')
482 if debug:
483 sys.stderr.write(out)
484 sys.stderr.write(err)
485 return output
486
487
488
489def indent_files(files, diff=False, debug=False, level=0, inplace=False):
490 output = []
491 for f in files:
492 dst = indent(f, debug=debug, level=level)
493 output.append([f,dst])
494
495 # First, copy to inplace
496 if inplace:
497 for src,dst in output:
498 shutil.copyfile(dst,src)
499 return True
500
501 # now, compare
502 failed = []
503 for src,dst in output:
504 if filecmp.cmp(src,dst) == 0:
505 failed.append([src, dst])
506 if len(failed) > 0:
507 if not diff:
508 print('Found %u badly indented files:' % len(failed))
509 for src,dst in failed:
510 print(' ' + src)
511 else:
512 for src,dst in failed:
513 with open(src, 'r') as f_src, open(dst, 'r') as f_dst:
514 s = f_src.readlines()
515 d = f_dst.readlines()
516 for line in difflib.unified_diff(s, d, fromfile=src, tofile=dst):
517 sys.stdout.write(line)
518 return False
519 return True
520
522 parser = optparse.OptionParser()
523 parser.add_option('--debug', action='store_true', dest='debug', default=False,
524 help='Output some debugging information')
525 parser.add_option('-l', '--level', type='int', dest='level', default=0,
526 help="Level of style conformance: higher levels include all lower levels. "
527 "level=0: re-indent only. level=1: add extra spaces. level=2: insert extra newlines and "
528 "extra braces around single-line statements. level=3: remove all trailing spaces")
529 parser.add_option('--check-git', action='store_true', dest='git', default=False,
530 help="Get the list of files to check from Git\'s list of modified and added files")
531 parser.add_option('-f', '--check-file', action='store', dest='file', default='',
532 help="Check a single file")
533 parser.add_option('--diff', action='store_true', dest='diff', default=False,
534 help="Generate a diff on stdout of the indented files")
535 parser.add_option('-i', '--in-place', action='store_true', dest='in_place', default=False,
536 help="Indent the input files in-place")
537 options, _ = parser.parse_args()
538 style_is_correct = False
539
540 if options.git:
541 files = git_modified_files()
542 style_is_correct = indent_files(files,
543 diff=options.diff,
544 debug=options.debug,
545 level=options.level,
546 inplace=options.in_place)
547 elif options.file != '':
548 file = options.file
549 if not os.path.exists(file) or \
550 not os.path.isfile(file):
551 print('file %s does not exist' % file)
552 sys.exit(1)
553 style_is_correct = indent_files([file],
554 diff=options.diff,
555 debug=options.debug,
556 level=options.level,
557 inplace=options.in_place)
558
559 if not style_is_correct:
560 sys.exit(1)
561 sys.exit(0)
562
563if __name__ == '__main__':
564 try:
566 except Exception as e:
567 sys.stderr.write(str(e) + '\n')
568 sys.exit(1)
PatchChunk class.
Definition: check-style.py:230
def dst(self)
Get destination lines.
Definition: check-style.py:273
def src(self)
Get source lines.
Definition: check-style.py:263
def dst_len(self)
Get number of destinaton lines.
Definition: check-style.py:289
def src_start(self)
Source start function.
Definition: check-style.py:250
def write(self, f)
Write lines to file.
Definition: check-style.py:295
__src_pos
source position
Definition: check-style.py:248
__lines
list of lines
Definition: check-style.py:247
def __init__(self, src_pos, dst_pos)
Initializer.
Definition: check-style.py:241
def add_line(self, line)
Add line function.
Definition: check-style.py:256
def src_len(self)
Get number of source lines.
Definition: check-style.py:283
__dst_pos
destination position
Definition: check-style.py:249
PatchChunkLine class.
Definition: check-style.py:145
def __init__(self)
Initializer.
Definition: check-style.py:159
def set_both(self, line)
Set both.
Definition: check-style.py:181
def line(self)
Get line.
Definition: check-style.py:196
def write(self, f)
Write to file.
Definition: check-style.py:214
def is_src(self)
Is source.
Definition: check-style.py:202
def set_dst(self, line)
Set destination.
Definition: check-style.py:173
def set_src(self, line)
Set source.
Definition: check-style.py:165
def is_dst(self)
Is destination.
Definition: check-style.py:208
def append_to_line(self, s)
Append to line.
Definition: check-style.py:189
Patch class.
Definition: check-style.py:307
def add_chunk(self, chunk)
Add chunk.
Definition: check-style.py:321
def chunks(self)
Get the chunks.
Definition: check-style.py:328
def set_src(self, src)
Set source.
Definition: check-style.py:334
__dst
destination
Definition: check-style.py:319
def set_dst(self, dst)
Set destination.
Definition: check-style.py:341
def apply(self, filename)
Apply function.
Definition: check-style.py:348
def write(self, f)
Write to file.
Definition: check-style.py:356
def __init__(self)
Initializer.
Definition: check-style.py:314
def parse_patchset(generator)
Definition: check-style.py:367
def uncrustify_config_file(level)
Definition: check-style.py:33
def remove_trailing_whitespace_changes(patch_generator)
Definition: check-style.py:413
def indent(source, debug, level)
Definition: check-style.py:432
def copy_file(filename)
Definition: check-style.py:25
def run_as_main()
Definition: check-style.py:521
def indent_files(files, diff=False, debug=False, level=0, inplace=False)
Definition: check-style.py:489
def git_modified_files()
Definition: check-style.py:13