lint.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #!/usr/bin/env python3
  2. import os
  3. import re
  4. import shutil
  5. import subprocess
  6. from flipper.app import App
  7. SOURCE_CODE_FILE_EXTENSIONS = [".h", ".c", ".cpp", ".cxx", ".hpp"]
  8. SOURCE_CODE_FILE_PATTERN = r"^[0-9A-Za-z_]+\.[a-z]+$"
  9. SOURCE_CODE_DIR_PATTERN = r"^[0-9A-Za-z_]+$"
  10. class Main(App):
  11. def init(self):
  12. self.subparsers = self.parser.add_subparsers(help="sub-command help")
  13. # generate
  14. self.parser_check = self.subparsers.add_parser(
  15. "check", help="Check source code format and file names"
  16. )
  17. self.parser_check.add_argument("input", nargs="+")
  18. self.parser_check.set_defaults(func=self.check)
  19. # merge
  20. self.parser_format = self.subparsers.add_parser(
  21. "format", help="Format source code and fix file names"
  22. )
  23. self.parser_format.add_argument(
  24. "input",
  25. nargs="+",
  26. )
  27. self.parser_format.set_defaults(func=self.format)
  28. def _check_folders(self, folders: list):
  29. show_message = False
  30. pattern = re.compile(SOURCE_CODE_DIR_PATTERN)
  31. for folder in folders:
  32. for dirpath, dirnames, filenames in os.walk(folder):
  33. for dirname in dirnames:
  34. if not pattern.match(dirname):
  35. to_fix = os.path.join(dirpath, dirname)
  36. self.logger.warning(f"Found incorrectly named folder {to_fix}")
  37. show_message = True
  38. if show_message:
  39. self.logger.warning(
  40. f"Folders are not renamed automatically, please fix it by yourself"
  41. )
  42. def _find_sources(self, folders: list):
  43. output = []
  44. for folder in folders:
  45. for dirpath, dirnames, filenames in os.walk(folder):
  46. for filename in filenames:
  47. ext = os.path.splitext(filename.lower())[1]
  48. if not ext in SOURCE_CODE_FILE_EXTENSIONS:
  49. continue
  50. output.append(os.path.join(dirpath, filename))
  51. return output
  52. def _format_sources(self, sources: list, dry_run: bool = False):
  53. args = ["clang-format", "--Werror", "--style=file", "-i"]
  54. if dry_run:
  55. args.append("--dry-run")
  56. args += sources
  57. try:
  58. subprocess.check_call(args)
  59. return True
  60. except subprocess.CalledProcessError as e:
  61. return False
  62. def _fix_filename(self, filename: str):
  63. return filename.replace("-", "_")
  64. def _replace_occurance(self, sources: list, old: str, new: str):
  65. old = old.encode()
  66. new = new.encode()
  67. for source in sources:
  68. content = open(source, "rb").read()
  69. if content.count(old) > 0:
  70. self.logger.info(f"Replacing {old} with {new} in {source}")
  71. content = content.replace(old, new)
  72. open(source, "wb").write(content)
  73. def _apply_file_naming_convention(self, sources: list, dry_run: bool = False):
  74. pattern = re.compile(SOURCE_CODE_FILE_PATTERN)
  75. good = []
  76. bad = []
  77. # Check sources for invalid filesname
  78. for source in sources:
  79. basename = os.path.basename(source)
  80. if not pattern.match(basename):
  81. new_basename = self._fix_filename(basename)
  82. if not pattern.match(new_basename):
  83. self.logger.error(f"Unable to fix name for {basename}")
  84. return False
  85. bad.append((source, basename, new_basename))
  86. else:
  87. good.append(source)
  88. # Notify about errors or replace all occurances
  89. if dry_run:
  90. if len(bad) > 0:
  91. self.logger.error(f"Found {len(bad)} incorrectly named files")
  92. return False
  93. else:
  94. # Replace occurances in text files
  95. for source, old, new in bad:
  96. self._replace_occurance(sources, old, new)
  97. # Rename files
  98. for source, old, new in bad:
  99. shutil.move(source, source.replace(old, new))
  100. return True
  101. def check(self):
  102. result = 0
  103. sources = self._find_sources(self.args.input)
  104. if not self._format_sources(sources, dry_run=True):
  105. result |= 0b01
  106. if not self._apply_file_naming_convention(sources, dry_run=True):
  107. result |= 0b10
  108. self._check_folders(self.args.input)
  109. return result
  110. def format(self):
  111. result = 0
  112. sources = self._find_sources(self.args.input)
  113. if not self._format_sources(sources):
  114. result |= 0b01
  115. if not self._apply_file_naming_convention(sources):
  116. result |= 0b10
  117. self._check_folders(self.args.input)
  118. return result
  119. if __name__ == "__main__":
  120. Main()()