PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/s2016/ns3-routing/ns-3/utils/check-style.py

https://gitlab.com/pmaddi/cs433
Python | 439 lines | 433 code | 4 blank | 2 comment | 4 complexity | 5d4f0d0516dc63552812b07fbf53dfa7 MD5 | raw file
  1. #!/usr/bin/env python
  2. import os
  3. import subprocess
  4. import tempfile
  5. import sys
  6. import filecmp
  7. import optparse
  8. import shutil
  9. import difflib
  10. import re
  11. def hg_modified_files():
  12. files = os.popen ('hg st -nma')
  13. return [filename.strip() for filename in files]
  14. def copy_file(filename):
  15. [tmp,pathname] = tempfile.mkstemp()
  16. src = open(filename, 'r')
  17. dst = open(pathname, 'w')
  18. for line in src:
  19. dst.write(line)
  20. dst.close()
  21. src.close()
  22. return pathname
  23. # generate a temporary configuration file
  24. def uncrustify_config_file(level):
  25. level2 = """
  26. nl_collapse_empty_body=False
  27. nl_if_brace=Add
  28. nl_brace_else=Add
  29. nl_elseif_brace=Add
  30. nl_else_brace=Add
  31. nl_while_brace=Add
  32. nl_do_brace=Add
  33. nl_for_brace=Add
  34. nl_brace_while=Add
  35. nl_switch_brace=Add
  36. nl_after_case=True
  37. nl_namespace_brace=Remove
  38. nl_after_brace_open=True
  39. nl_class_leave_one_liners=False
  40. nl_enum_leave_one_liners=False
  41. nl_func_leave_one_liners=False
  42. nl_if_leave_one_liners=False
  43. nl_class_colon=Ignore
  44. nl_after_access_spec=1
  45. nl_after_semicolon=True
  46. pos_class_colon=Lead
  47. pos_class_comma=Trail
  48. pos_bool=Lead
  49. nl_class_init_args=Add
  50. nl_template_class=Add
  51. nl_class_brace=Add
  52. # does not work very well
  53. nl_func_type_name=Ignore
  54. nl_func_scope_name=Ignore
  55. nl_func_type_name_class=Ignore
  56. nl_func_proto_type_name=Ignore
  57. # function\\n(
  58. nl_func_paren=Remove
  59. nl_fdef_brace=Add
  60. nl_struct_brace=Add
  61. nl_enum_brace=Add
  62. nl_union_brace=Add
  63. mod_full_brace_do=Add
  64. mod_full_brace_for=Add
  65. mod_full_brace_if=Add
  66. mod_full_brace_while=Add
  67. mod_full_brace_for=Add
  68. mod_remove_extra_semicolon=True
  69. # max code width
  70. #code_width=128
  71. #ls_for_split_full=True
  72. #ls_func_split_full=True
  73. """
  74. level1 = """
  75. # extra spaces here and there
  76. sp_func_proto_paren=Add
  77. sp_func_def_paren=Add
  78. sp_func_call_paren=Add
  79. sp_brace_typedef=Add
  80. sp_enum_assign=Add
  81. sp_before_sparen=Add
  82. sp_after_semi_for=Add
  83. sp_arith=Add
  84. sp_assign=Add
  85. sp_compare=Add
  86. sp_cmt_cpp_start=Add
  87. sp_func_class_paren=Add
  88. sp_after_type=Add
  89. sp_type_func=Add
  90. sp_angle_paren=Add
  91. """
  92. level0 = """
  93. sp_after_semi_for=Ignore
  94. sp_before_sparen=Ignore
  95. sp_type_func=Ignore
  96. sp_after_type=Ignore
  97. nl_class_leave_one_liners=True
  98. nl_enum_leave_one_liners=True
  99. nl_func_leave_one_liners=True
  100. nl_assign_leave_one_liners=True
  101. #nl_collapse_empty_body=False
  102. nl_getset_leave_one_liners=True
  103. nl_if_leave_one_liners=True
  104. nl_fdef_brace=Ignore
  105. # finally, indentation configuration
  106. indent_with_tabs=0
  107. indent_namespace=false
  108. indent_columns=2
  109. indent_brace=2
  110. indent_case_brace=2
  111. indent_class=true
  112. indent_class_colon=True
  113. # alignment
  114. indent_align_assign=False
  115. align_left_shift=True
  116. # comment reformating disabled
  117. cmt_reflow_mode=1 # do not touch comments at all
  118. cmt_indent_multi=False # really, do not touch them
  119. """
  120. [tmp,pathname] = tempfile.mkstemp()
  121. dst = open(pathname, 'w')
  122. dst.write(level0)
  123. if level >= 1:
  124. dst.write(level1)
  125. if level >= 2:
  126. dst.write(level2)
  127. dst.close()
  128. return pathname
  129. class PatchChunkLine:
  130. SRC = 1
  131. DST = 2
  132. BOTH = 3
  133. def __init__(self):
  134. self.__type = 0
  135. self.__line = ''
  136. def set_src(self,line):
  137. self.__type = self.SRC
  138. self.__line = line
  139. def set_dst(self,line):
  140. self.__type = self.DST
  141. self.__line = line
  142. def set_both(self,line):
  143. self.__type = self.BOTH
  144. self.__line = line
  145. def append_to_line(self, s):
  146. self.__line = self.__line + s
  147. def line(self):
  148. return self.__line
  149. def is_src(self):
  150. return self.__type == self.SRC or self.__type == self.BOTH
  151. def is_dst(self):
  152. return self.__type == self.DST or self.__type == self.BOTH
  153. def write(self, f):
  154. if self.__type == self.SRC:
  155. f.write('-%s\n' % self.__line)
  156. elif self.__type == self.DST:
  157. f.write('+%s\n' % self.__line)
  158. elif self.__type == self.BOTH:
  159. f.write(' %s\n' % self.__line)
  160. else:
  161. raise Exception('invalid patch')
  162. class PatchChunk:
  163. def __init__(self, src_pos, dst_pos):
  164. self.__lines = []
  165. self.__src_pos = int(src_pos)
  166. self.__dst_pos = int(dst_pos)
  167. def src_start(self):
  168. return self.__src_pos
  169. def add_line(self,line):
  170. self.__lines.append(line)
  171. def src(self):
  172. src = []
  173. for line in self.__lines:
  174. if line.is_src():
  175. src.append(line)
  176. return src
  177. def dst(self):
  178. dst = []
  179. for line in self.__lines:
  180. if line.is_dst():
  181. dst.append(line)
  182. return dst
  183. def src_len(self):
  184. return len(self.src())
  185. def dst_len(self):
  186. return len(self.dst())
  187. def write(self,f):
  188. f.write('@@ -%d,%d +%d,%d @@\n' % (self.__src_pos, self.src_len(),
  189. self.__dst_pos, self.dst_len()))
  190. for line in self.__lines:
  191. line.write(f)
  192. class Patch:
  193. def __init__(self):
  194. self.__src = ''
  195. self.__dst = ''
  196. self.__chunks = []
  197. def add_chunk(self, chunk):
  198. self.__chunks.append(chunk)
  199. def chunks(self):
  200. return self.__chunks
  201. def set_src(self,src):
  202. self.__src = src
  203. def set_dst(self,dst):
  204. self.__dst = dst
  205. def apply(self,filename):
  206. # XXX: not implemented
  207. return
  208. def write(self,f):
  209. f.write('--- %s\n' % self.__src )
  210. f.write('+++ %s\n' % self.__dst )
  211. for chunk in self.__chunks:
  212. chunk.write(f)
  213. def parse_patchset(generator):
  214. src_file = re.compile('^--- (.*)$')
  215. dst_file = re.compile('^\+\+\+ (.*)$')
  216. chunk_start = re.compile('^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@')
  217. src = re.compile('^-(.*)$')
  218. dst = re.compile('^\+(.*)$')
  219. both = re.compile('^ (.*)$')
  220. patchset = []
  221. current_patch = None
  222. for line in generator:
  223. m = src_file.search(line)
  224. if m is not None:
  225. current_patch = Patch()
  226. patchset.append(current_patch)
  227. current_patch.set_src(m.group(1))
  228. continue
  229. m = dst_file.search(line)
  230. if m is not None:
  231. current_patch.set_dst(m.group(1))
  232. continue
  233. m = chunk_start.search(line)
  234. if m is not None:
  235. current_chunk = PatchChunk(m.group(1), m.group(3))
  236. current_patch.add_chunk(current_chunk)
  237. continue
  238. m = src.search(line)
  239. if m is not None:
  240. l = PatchChunkLine()
  241. l.set_src(m.group(1))
  242. current_chunk.add_line(l)
  243. continue
  244. m = dst.search(line)
  245. if m is not None:
  246. l = PatchChunkLine()
  247. l.set_dst(m.group(1))
  248. current_chunk.add_line(l)
  249. continue
  250. m = both.search(line)
  251. if m is not None:
  252. l = PatchChunkLine()
  253. l.set_both(m.group(1))
  254. current_chunk.add_line(l)
  255. continue
  256. raise Exception()
  257. return patchset
  258. def remove_trailing_whitespace_changes(patch_generator):
  259. whitespace = re.compile('^(.*)([ \t]+)$')
  260. patchset = parse_patchset(patch_generator)
  261. for patch in patchset:
  262. for chunk in patch.chunks():
  263. src = chunk.src()
  264. dst = chunk.dst()
  265. try:
  266. for i in range(0,len(src)):
  267. s = src[i]
  268. d = dst[i]
  269. m = whitespace.search(s.line())
  270. if m is not None and m.group(1) == d.line():
  271. d.append_to_line(m.group(2))
  272. except:
  273. return patchset
  274. return patchset
  275. def indent(source, debug, level):
  276. output = tempfile.mkstemp()[1]
  277. # apply uncrustify
  278. cfg = uncrustify_config_file(level)
  279. if debug:
  280. sys.stderr.write('original file=' + source + '\n')
  281. sys.stderr.write('uncrustify config file=' + cfg + '\n')
  282. sys.stderr.write('temporary file=' + output + '\n')
  283. try:
  284. uncrust = subprocess.Popen(['uncrustify', '-c', cfg, '-f', source, '-o', output],
  285. stdin = subprocess.PIPE,
  286. stdout = subprocess.PIPE,
  287. stderr = subprocess.PIPE)
  288. (out, err) = uncrust.communicate('')
  289. if debug:
  290. sys.stderr.write(out)
  291. sys.stderr.write(err)
  292. except OSError:
  293. raise Exception ('uncrustify not installed')
  294. # generate a diff file
  295. src = open(source, 'r')
  296. dst = open(output, 'r')
  297. diff = difflib.unified_diff(src.readlines(), dst.readlines(),
  298. fromfile=source, tofile=output)
  299. src.close()
  300. dst.close()
  301. if debug:
  302. initial_diff = tempfile.mkstemp()[1]
  303. sys.stderr.write('initial diff file=' + initial_diff + '\n')
  304. tmp = open(initial_diff, 'w')
  305. tmp.writelines(diff)
  306. tmp.close()
  307. final_diff = tempfile.mkstemp()[1]
  308. if level < 3:
  309. patchset = remove_trailing_whitespace_changes(diff);
  310. dst = open(final_diff, 'w')
  311. if len(patchset) != 0:
  312. patchset[0].write(dst)
  313. dst.close()
  314. else:
  315. dst = open(final_diff, 'w')
  316. dst.writelines(diff)
  317. dst.close()
  318. # apply diff file
  319. if debug:
  320. sys.stderr.write('final diff file=' + final_diff + '\n')
  321. shutil.copyfile(source,output)
  322. patch = subprocess.Popen(['patch', '-p1', '-i', final_diff, output],
  323. stdin = subprocess.PIPE,
  324. stdout = subprocess.PIPE,
  325. stderr = subprocess.PIPE)
  326. (out, err) = patch.communicate('')
  327. if debug:
  328. sys.stderr.write(out)
  329. sys.stderr.write(err)
  330. return output
  331. def indent_files(files, diff=False, debug=False, level=0, inplace=False):
  332. output = []
  333. for f in files:
  334. dst = indent(f, debug=debug, level=level)
  335. output.append([f,dst])
  336. # First, copy to inplace
  337. if inplace:
  338. for src,dst in output:
  339. shutil.copyfile(dst,src)
  340. return True
  341. # now, compare
  342. failed = []
  343. for src,dst in output:
  344. if filecmp.cmp(src,dst) == 0:
  345. failed.append([src, dst])
  346. if len(failed) > 0:
  347. if not diff:
  348. print 'Found %u badly indented files:' % len(failed)
  349. for src,dst in failed:
  350. print ' ' + src
  351. else:
  352. for src,dst in failed:
  353. s = open(src, 'r').readlines()
  354. d = open(dst, 'r').readlines()
  355. for line in difflib.unified_diff(s, d, fromfile=src, tofile=dst):
  356. sys.stdout.write(line)
  357. return False
  358. return True
  359. def run_as_hg_hook(ui, repo, **kwargs):
  360. # hack to work around mercurial < 1.3 bug
  361. from mercurial import lock, error
  362. lock.LockError = error.LockError
  363. # actually do the work
  364. files = hg_modified_files()
  365. if not indent_files(files, inplace=False):
  366. return True
  367. return False
  368. def run_as_main():
  369. parser = optparse.OptionParser()
  370. parser.add_option('--debug', action='store_true', dest='debug', default=False,
  371. help='Output some debugging information')
  372. parser.add_option('-l', '--level', type='int', dest='level', default=0,
  373. help="Level of style conformance: higher levels include all lower levels. "
  374. "level=0: re-indent only. level=1: add extra spaces. level=2: insert extra newlines and "
  375. "extra braces around single-line statements. level=3: remove all trailing spaces")
  376. parser.add_option('--check-hg-hook', action='store_true', dest='hg_hook', default=False,
  377. help='Get the list of files to check from mercurial\'s list of modified '
  378. 'and added files and assume that the script runs as a pretxncommit mercurial hook')
  379. parser.add_option('--check-hg', action='store_true', dest='hg', default=False,
  380. help="Get the list of files to check from mercurial\'s list of modified and added files")
  381. parser.add_option('-f', '--check-file', action='store', dest='file', default='',
  382. help="Check a single file")
  383. parser.add_option('--diff', action='store_true', dest='diff', default=False,
  384. help="Generate a diff on stdout of the indented files")
  385. parser.add_option('-i', '--in-place', action='store_true', dest='in_place', default=False,
  386. help="Indent the input files in-place")
  387. (options,args) = parser.parse_args()
  388. debug = options.debug
  389. if options.hg_hook:
  390. files = hg_modified_files()
  391. if not indent_files(files, debug=options.debug,
  392. level=options.level,
  393. inplace=False):
  394. sys.exit(1)
  395. elif options.hg:
  396. files = hg_modified_files()
  397. indent_files(files, diff=options.diff,
  398. debug=options.debug,
  399. level=options.level,
  400. inplace=options.in_place)
  401. elif options.file != '':
  402. file = options.file
  403. if not os.path.exists(file) or \
  404. not os.path.isfile(file):
  405. print 'file %s does not exist' % file
  406. sys.exit(1)
  407. indent_files([file], diff=options.diff,
  408. debug=options.debug,
  409. level=options.level,
  410. inplace=options.in_place)
  411. sys.exit(0)
  412. if __name__ == '__main__':
  413. # try:
  414. run_as_main()
  415. # except Exception, e:
  416. # sys.stderr.write(str(e) + '\n')
  417. # sys.exit(1)