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