/s2016/ns3-routing/ns-3/utils/check-style.py
Python | 439 lines | 433 code | 4 blank | 2 comment | 4 complexity | 5d4f0d0516dc63552812b07fbf53dfa7 MD5 | raw file
- #!/usr/bin/env python
- import os
- import subprocess
- import tempfile
- import sys
- import filecmp
- import optparse
- import shutil
- import difflib
- import re
- def hg_modified_files():
- files = os.popen ('hg st -nma')
- return [filename.strip() for filename in files]
- def copy_file(filename):
- [tmp,pathname] = tempfile.mkstemp()
- src = open(filename, 'r')
- dst = open(pathname, 'w')
- for line in src:
- dst.write(line)
- dst.close()
- src.close()
- return pathname
- # generate a temporary configuration file
- def uncrustify_config_file(level):
- level2 = """
- nl_collapse_empty_body=False
- nl_if_brace=Add
- nl_brace_else=Add
- nl_elseif_brace=Add
- nl_else_brace=Add
- nl_while_brace=Add
- nl_do_brace=Add
- nl_for_brace=Add
- nl_brace_while=Add
- nl_switch_brace=Add
- nl_after_case=True
- nl_namespace_brace=Remove
- nl_after_brace_open=True
- nl_class_leave_one_liners=False
- nl_enum_leave_one_liners=False
- nl_func_leave_one_liners=False
- nl_if_leave_one_liners=False
- nl_class_colon=Ignore
- nl_after_access_spec=1
- nl_after_semicolon=True
- pos_class_colon=Lead
- pos_class_comma=Trail
- pos_bool=Lead
- nl_class_init_args=Add
- nl_template_class=Add
- nl_class_brace=Add
- # does not work very well
- nl_func_type_name=Ignore
- nl_func_scope_name=Ignore
- nl_func_type_name_class=Ignore
- nl_func_proto_type_name=Ignore
- # function\\n(
- nl_func_paren=Remove
- nl_fdef_brace=Add
- nl_struct_brace=Add
- nl_enum_brace=Add
- nl_union_brace=Add
- mod_full_brace_do=Add
- mod_full_brace_for=Add
- mod_full_brace_if=Add
- mod_full_brace_while=Add
- mod_full_brace_for=Add
- mod_remove_extra_semicolon=True
- # max code width
- #code_width=128
- #ls_for_split_full=True
- #ls_func_split_full=True
- """
- level1 = """
- # extra spaces here and there
- sp_func_proto_paren=Add
- sp_func_def_paren=Add
- sp_func_call_paren=Add
- sp_brace_typedef=Add
- sp_enum_assign=Add
- sp_before_sparen=Add
- sp_after_semi_for=Add
- sp_arith=Add
- sp_assign=Add
- sp_compare=Add
- sp_cmt_cpp_start=Add
- sp_func_class_paren=Add
- sp_after_type=Add
- sp_type_func=Add
- sp_angle_paren=Add
- """
- level0 = """
- sp_after_semi_for=Ignore
- sp_before_sparen=Ignore
- sp_type_func=Ignore
- sp_after_type=Ignore
- nl_class_leave_one_liners=True
- nl_enum_leave_one_liners=True
- nl_func_leave_one_liners=True
- nl_assign_leave_one_liners=True
- #nl_collapse_empty_body=False
- nl_getset_leave_one_liners=True
- nl_if_leave_one_liners=True
- nl_fdef_brace=Ignore
- # finally, indentation configuration
- indent_with_tabs=0
- indent_namespace=false
- indent_columns=2
- indent_brace=2
- indent_case_brace=2
- indent_class=true
- indent_class_colon=True
- # alignment
- indent_align_assign=False
- align_left_shift=True
- # comment reformating disabled
- cmt_reflow_mode=1 # do not touch comments at all
- cmt_indent_multi=False # really, do not touch them
- """
- [tmp,pathname] = tempfile.mkstemp()
- dst = open(pathname, 'w')
- dst.write(level0)
- if level >= 1:
- dst.write(level1)
- if level >= 2:
- dst.write(level2)
- dst.close()
- return pathname
- class PatchChunkLine:
- SRC = 1
- DST = 2
- BOTH = 3
- def __init__(self):
- self.__type = 0
- self.__line = ''
- def set_src(self,line):
- self.__type = self.SRC
- self.__line = line
- def set_dst(self,line):
- self.__type = self.DST
- self.__line = line
- def set_both(self,line):
- self.__type = self.BOTH
- self.__line = line
- def append_to_line(self, s):
- self.__line = self.__line + s
- def line(self):
- return self.__line
- def is_src(self):
- return self.__type == self.SRC or self.__type == self.BOTH
- def is_dst(self):
- return self.__type == self.DST or self.__type == self.BOTH
- def write(self, f):
- if self.__type == self.SRC:
- f.write('-%s\n' % self.__line)
- elif self.__type == self.DST:
- f.write('+%s\n' % self.__line)
- elif self.__type == self.BOTH:
- f.write(' %s\n' % self.__line)
- else:
- raise Exception('invalid patch')
-
- class PatchChunk:
- def __init__(self, src_pos, dst_pos):
- self.__lines = []
- self.__src_pos = int(src_pos)
- self.__dst_pos = int(dst_pos)
- def src_start(self):
- return self.__src_pos
- def add_line(self,line):
- self.__lines.append(line)
- def src(self):
- src = []
- for line in self.__lines:
- if line.is_src():
- src.append(line)
- return src
- def dst(self):
- dst = []
- for line in self.__lines:
- if line.is_dst():
- dst.append(line)
- return dst
- def src_len(self):
- return len(self.src())
- def dst_len(self):
- return len(self.dst())
- def write(self,f):
- f.write('@@ -%d,%d +%d,%d @@\n' % (self.__src_pos, self.src_len(),
- self.__dst_pos, self.dst_len()))
- for line in self.__lines:
- line.write(f)
- class Patch:
- def __init__(self):
- self.__src = ''
- self.__dst = ''
- self.__chunks = []
- def add_chunk(self, chunk):
- self.__chunks.append(chunk)
- def chunks(self):
- return self.__chunks
- def set_src(self,src):
- self.__src = src
- def set_dst(self,dst):
- self.__dst = dst
- def apply(self,filename):
- # XXX: not implemented
- return
- def write(self,f):
- f.write('--- %s\n' % self.__src )
- f.write('+++ %s\n' % self.__dst )
- for chunk in self.__chunks:
- chunk.write(f)
- def parse_patchset(generator):
- src_file = re.compile('^--- (.*)$')
- dst_file = re.compile('^\+\+\+ (.*)$')
- chunk_start = re.compile('^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@')
- src = re.compile('^-(.*)$')
- dst = re.compile('^\+(.*)$')
- both = re.compile('^ (.*)$')
- patchset = []
- current_patch = None
- for line in generator:
- m = src_file.search(line)
- if m is not None:
- current_patch = Patch()
- patchset.append(current_patch)
- current_patch.set_src(m.group(1))
- continue
- m = dst_file.search(line)
- if m is not None:
- current_patch.set_dst(m.group(1))
- continue
- m = chunk_start.search(line)
- if m is not None:
- current_chunk = PatchChunk(m.group(1), m.group(3))
- current_patch.add_chunk(current_chunk)
- continue
- m = src.search(line)
- if m is not None:
- l = PatchChunkLine()
- l.set_src(m.group(1))
- current_chunk.add_line(l)
- continue
- m = dst.search(line)
- if m is not None:
- l = PatchChunkLine()
- l.set_dst(m.group(1))
- current_chunk.add_line(l)
- continue
- m = both.search(line)
- if m is not None:
- l = PatchChunkLine()
- l.set_both(m.group(1))
- current_chunk.add_line(l)
- continue
- raise Exception()
- return patchset
- def remove_trailing_whitespace_changes(patch_generator):
- whitespace = re.compile('^(.*)([ \t]+)$')
- patchset = parse_patchset(patch_generator)
- for patch in patchset:
- for chunk in patch.chunks():
- src = chunk.src()
- dst = chunk.dst()
- try:
- for i in range(0,len(src)):
- s = src[i]
- d = dst[i]
- m = whitespace.search(s.line())
- if m is not None and m.group(1) == d.line():
- d.append_to_line(m.group(2))
- except:
- return patchset
- return patchset
- def indent(source, debug, level):
- output = tempfile.mkstemp()[1]
- # apply uncrustify
- cfg = uncrustify_config_file(level)
- if debug:
- sys.stderr.write('original file=' + source + '\n')
- sys.stderr.write('uncrustify config file=' + cfg + '\n')
- sys.stderr.write('temporary file=' + output + '\n')
- try:
- uncrust = subprocess.Popen(['uncrustify', '-c', cfg, '-f', source, '-o', output],
- stdin = subprocess.PIPE,
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE)
- (out, err) = uncrust.communicate('')
- if debug:
- sys.stderr.write(out)
- sys.stderr.write(err)
- except OSError:
- raise Exception ('uncrustify not installed')
- # generate a diff file
- src = open(source, 'r')
- dst = open(output, 'r')
- diff = difflib.unified_diff(src.readlines(), dst.readlines(),
- fromfile=source, tofile=output)
- src.close()
- dst.close()
- if debug:
- initial_diff = tempfile.mkstemp()[1]
- sys.stderr.write('initial diff file=' + initial_diff + '\n')
- tmp = open(initial_diff, 'w')
- tmp.writelines(diff)
- tmp.close()
- final_diff = tempfile.mkstemp()[1]
- if level < 3:
- patchset = remove_trailing_whitespace_changes(diff);
- dst = open(final_diff, 'w')
- if len(patchset) != 0:
- patchset[0].write(dst)
- dst.close()
- else:
- dst = open(final_diff, 'w')
- dst.writelines(diff)
- dst.close()
-
-
- # apply diff file
- if debug:
- sys.stderr.write('final diff file=' + final_diff + '\n')
- shutil.copyfile(source,output)
- patch = subprocess.Popen(['patch', '-p1', '-i', final_diff, output],
- stdin = subprocess.PIPE,
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE)
- (out, err) = patch.communicate('')
- if debug:
- sys.stderr.write(out)
- sys.stderr.write(err)
- return output
-
- def indent_files(files, diff=False, debug=False, level=0, inplace=False):
- output = []
- for f in files:
- dst = indent(f, debug=debug, level=level)
- output.append([f,dst])
- # First, copy to inplace
- if inplace:
- for src,dst in output:
- shutil.copyfile(dst,src)
- return True
- # now, compare
- failed = []
- for src,dst in output:
- if filecmp.cmp(src,dst) == 0:
- failed.append([src, dst])
- if len(failed) > 0:
- if not diff:
- print 'Found %u badly indented files:' % len(failed)
- for src,dst in failed:
- print ' ' + src
- else:
- for src,dst in failed:
- s = open(src, 'r').readlines()
- d = open(dst, 'r').readlines()
- for line in difflib.unified_diff(s, d, fromfile=src, tofile=dst):
- sys.stdout.write(line)
- return False
- return True
- def run_as_hg_hook(ui, repo, **kwargs):
- # hack to work around mercurial < 1.3 bug
- from mercurial import lock, error
- lock.LockError = error.LockError
- # actually do the work
- files = hg_modified_files()
- if not indent_files(files, inplace=False):
- return True
- return False
- def run_as_main():
- parser = optparse.OptionParser()
- parser.add_option('--debug', action='store_true', dest='debug', default=False,
- help='Output some debugging information')
- parser.add_option('-l', '--level', type='int', dest='level', default=0,
- help="Level of style conformance: higher levels include all lower levels. "
- "level=0: re-indent only. level=1: add extra spaces. level=2: insert extra newlines and "
- "extra braces around single-line statements. level=3: remove all trailing spaces")
- parser.add_option('--check-hg-hook', action='store_true', dest='hg_hook', default=False,
- help='Get the list of files to check from mercurial\'s list of modified '
- 'and added files and assume that the script runs as a pretxncommit mercurial hook')
- parser.add_option('--check-hg', action='store_true', dest='hg', default=False,
- help="Get the list of files to check from mercurial\'s list of modified and added files")
- parser.add_option('-f', '--check-file', action='store', dest='file', default='',
- help="Check a single file")
- parser.add_option('--diff', action='store_true', dest='diff', default=False,
- help="Generate a diff on stdout of the indented files")
- parser.add_option('-i', '--in-place', action='store_true', dest='in_place', default=False,
- help="Indent the input files in-place")
- (options,args) = parser.parse_args()
- debug = options.debug
- if options.hg_hook:
- files = hg_modified_files()
- if not indent_files(files, debug=options.debug,
- level=options.level,
- inplace=False):
- sys.exit(1)
- elif options.hg:
- files = hg_modified_files()
- indent_files(files, diff=options.diff,
- debug=options.debug,
- level=options.level,
- inplace=options.in_place)
- elif options.file != '':
- file = options.file
- if not os.path.exists(file) or \
- not os.path.isfile(file):
- print 'file %s does not exist' % file
- sys.exit(1)
- indent_files([file], diff=options.diff,
- debug=options.debug,
- level=options.level,
- inplace=options.in_place)
- sys.exit(0)
- if __name__ == '__main__':
- # try:
- run_as_main()
- # except Exception, e:
- # sys.stderr.write(str(e) + '\n')
- # sys.exit(1)