Skip to content

Commit 2dc1619

Browse files
authored
Merge pull request #4 from flyingcys/dev-check
update check_format.py
2 parents 32fe48c + 2d54628 commit 2dc1619

1 file changed

Lines changed: 119 additions & 58 deletions

File tree

tools/check_format.py

Lines changed: 119 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Code format checker script
44
Used to check if modified C/C++ files in PR comply with clang-format standards,
55
detect Chinese characters in code, and validate file headers
6+
7+
Support modes:
8+
- PR mode (default): Check files modified in PR relative to base branch
9+
- Debug mode: Check specified local files or directories for development
610
"""
711

812
import os
@@ -13,11 +17,13 @@
1317
import re
1418
from pathlib import Path
1519
from datetime import datetime
20+
import glob
1621

1722

1823
class FormatChecker:
19-
def __init__(self, base_ref="master"):
24+
def __init__(self, base_ref="master", debug_mode=False):
2025
self.base_ref = base_ref
26+
self.debug_mode = debug_mode
2127
self.project_root = self._find_project_root()
2228
self.clang_format_ignore = self._load_ignore_patterns()
2329
self.current_year = datetime.now().year
@@ -81,6 +87,52 @@ def _get_changed_files(self):
8187
print(f"Error: Failed to execute git command: {e}")
8288
return []
8389

90+
def _get_local_files(self, file_patterns=None, directories=None):
91+
"""Get local C/C++ files for debug mode"""
92+
files = []
93+
c_cpp_extensions = ('.c', '.cpp', '.h', '.hpp', '.cc', '.cxx')
94+
95+
# Process specified files
96+
if file_patterns:
97+
for pattern in file_patterns:
98+
# Support glob patterns
99+
matched_files = glob.glob(pattern, recursive=True)
100+
for file_path in matched_files:
101+
path_obj = Path(file_path)
102+
if path_obj.is_file() and path_obj.suffix in c_cpp_extensions:
103+
rel_path = path_obj.relative_to(self.project_root) if path_obj.is_absolute() else path_obj
104+
if not self._should_ignore_file(str(rel_path)):
105+
files.append(str(rel_path))
106+
107+
# Process specified directories
108+
if directories:
109+
for directory in directories:
110+
dir_path = Path(directory)
111+
if not dir_path.exists():
112+
print(f"Warning: Directory does not exist: {directory}")
113+
continue
114+
115+
# Find all C/C++ files in directory recursively
116+
for ext in c_cpp_extensions:
117+
pattern = f"**/*{ext}"
118+
for file_path in dir_path.glob(pattern):
119+
rel_path = file_path.relative_to(self.project_root) if file_path.is_absolute() else file_path
120+
if not self._should_ignore_file(str(rel_path)):
121+
files.append(str(rel_path))
122+
123+
# If no files or directories specified, scan current directory
124+
if not file_patterns and not directories:
125+
current_dir = Path.cwd()
126+
for ext in c_cpp_extensions:
127+
pattern = f"**/*{ext}"
128+
for file_path in current_dir.glob(pattern):
129+
rel_path = file_path.relative_to(self.project_root) if file_path.is_absolute() else file_path
130+
if not self._should_ignore_file(str(rel_path)):
131+
files.append(str(rel_path))
132+
133+
# Remove duplicates and return sorted list
134+
return sorted(list(set(files)))
135+
84136
def _check_clang_format_available(self):
85137
"""Check if clang-format is available"""
86138
try:
@@ -125,10 +177,7 @@ def _show_diff(self, file_path, original, formatted):
125177

126178
diff_content = ''.join(diff)
127179
if diff_content:
128-
print(f"\nFile {file_path} does not conform to format:")
129-
print("=" * 60)
130-
print(diff_content)
131-
print("=" * 60)
180+
print(f"❌ File {file_path} does not conform to format standards")
132181
return True
133182
return False
134183

@@ -163,12 +212,7 @@ def _check_chinese_characters(self, file_path):
163212
def _show_chinese_errors(self, file_path, chinese_errors):
164213
"""Show Chinese character errors for file"""
165214
if chinese_errors:
166-
print(f"\nFile {file_path} contains Chinese characters:")
167-
print("=" * 60)
168-
for error in chinese_errors:
169-
print(f"Line {error['line']}, Column {error['column']}: '{error['character']}'")
170-
print(f" Context: {error['context']}")
171-
print("=" * 60)
215+
print(f"❌ File {file_path} contains Chinese characters")
172216
return True
173217
return False
174218

@@ -238,49 +282,38 @@ def _check_file_header(self, file_path):
238282
def _show_header_errors(self, file_path, header_errors):
239283
"""Show file header errors"""
240284
if header_errors:
241-
print(f"\nFile {file_path} has header issues:")
242-
print("=" * 60)
243-
for error in header_errors:
244-
print(f" - {error}")
245-
print("\nExpected header format:")
246-
print("/**")
247-
print(" * @file filename.c")
248-
print(" * @brief Brief description of the file")
249-
print(" *")
250-
print(" * Detailed description...")
251-
print(" *")
252-
print(f" * @copyright Copyright (c) 2021-{self.current_year} Tuya Inc. All Rights Reserved.")
253-
print(" */")
254-
print("=" * 60)
285+
print(f"❌ File {file_path} has invalid header format")
255286
return True
256287
return False
257288

258289
def _show_header_warnings(self, file_path, header_warnings):
259290
"""Show file header warnings"""
260291
if header_warnings:
261-
print(f"\nFile {file_path} has header suggestions:")
262-
print("~" * 60)
263-
for warning in header_warnings:
264-
print(f" ⚠️ {warning}")
265-
print("~" * 60)
292+
print(f"⚠️ File {file_path} has header suggestions")
266293
return True
267294
return False
268295

269-
def check_format(self):
296+
def check_format(self, file_patterns=None, directories=None):
270297
"""Check code format, Chinese characters, and file headers"""
271298
if not self._check_clang_format_available():
272299
print("Error: clang-format is not installed or not in PATH")
273300
print("Please install clang-format (recommended version 14)")
274301
return False
275302

276-
print(f"Checking files modified relative to {self.base_ref} branch...")
277-
278-
changed_files = self._get_changed_files()
279-
if not changed_files:
280-
print("No C/C++ files need to be checked")
281-
return True
303+
if self.debug_mode:
304+
print("🔧 Debug mode: Checking local files...")
305+
changed_files = self._get_local_files(file_patterns, directories)
306+
if not changed_files:
307+
print("❌ No C/C++ files found matching the criteria")
308+
return True
309+
else:
310+
print(f"📋 PR mode: Checking files modified relative to {self.base_ref} branch...")
311+
changed_files = self._get_changed_files()
312+
if not changed_files:
313+
print("✅ No C/C++ files need to be checked")
314+
return True
282315

283-
print(f"Found {len(changed_files)} modified C/C++ files:")
316+
print(f"📁 Found {len(changed_files)} C/C++ file(s) to check:")
284317
for file_path in changed_files:
285318
print(f" - {file_path}")
286319

@@ -289,7 +322,6 @@ def check_format(self):
289322
header_errors = []
290323
header_warnings = []
291324

292-
print("\n--- Checking code format ---")
293325
for file_path in changed_files:
294326
full_path = self.project_root / file_path
295327
original, formatted = self._format_file_content(full_path)
@@ -298,7 +330,6 @@ def check_format(self):
298330
if self._show_diff(file_path, original, formatted):
299331
format_errors.append(file_path)
300332

301-
print("\n--- Checking Chinese characters ---")
302333
for file_path in changed_files:
303334
full_path = self.project_root / file_path
304335
chinese_issues = self._check_chinese_characters(full_path)
@@ -307,7 +338,6 @@ def check_format(self):
307338
if self._show_chinese_errors(file_path, chinese_issues):
308339
chinese_errors.append(file_path)
309340

310-
print("\n--- Checking file headers ---")
311341
for file_path in changed_files:
312342
full_path = self.project_root / file_path
313343
errors, warnings = self._check_file_header(full_path)
@@ -323,56 +353,87 @@ def check_format(self):
323353
has_errors = format_errors or chinese_errors or header_errors
324354

325355
if format_errors:
326-
print(f"\n❌ Found {len(format_errors)} files that do not conform to format:")
356+
print(f"\n❌ Found {len(format_errors)} file(s) that do not conform to format:")
327357
for file_path in format_errors:
328358
print(f" - {file_path}")
329-
print("\nPlease run the following command to fix format issues:")
330-
print("clang-format -style=file -i " + " ".join(format_errors))
331359

332360
if chinese_errors:
333-
print(f"\n❌ Found {len(chinese_errors)} files containing Chinese characters:")
361+
print(f"\n❌ Found {len(chinese_errors)} file(s) containing Chinese characters:")
334362
for file_path in chinese_errors:
335363
print(f" - {file_path}")
336-
print("\nPlease remove all Chinese characters from the code.")
337-
print("Chinese comments and text are not allowed in the codebase.")
338364

339365
if header_errors:
340-
print(f"\n❌ Found {len(header_errors)} files with header issues:")
366+
print(f"\n❌ Found {len(header_errors)} file(s) with header issues:")
341367
for file_path in header_errors:
342368
print(f" - {file_path}")
343-
print(f"\nPlease update file headers to include:")
344-
print("- Proper @file, @brief, and @copyright tags")
345-
print(f"- Copyright year ending with {self.current_year}")
346369

347370
if header_warnings:
348-
print(f"\n⚠️ Found {len(header_warnings)} files with header suggestions:")
371+
print(f"\n⚠️ Found {len(header_warnings)} file(s) with header suggestions:")
349372
for file_path in header_warnings:
350373
print(f" - {file_path}")
351374

352375
if not has_errors and not header_warnings:
353-
print("\n✅ All modified files conform to format standards, contain no Chinese characters, and have proper headers!")
376+
print("\n✅ All files conform to format standards, contain no Chinese characters, and have proper headers!")
354377
elif not has_errors:
355-
print("\n✅ All modified files pass required checks! (Some suggestions above)")
378+
print("\n✅ All files pass required checks! (Some suggestions above)")
356379

357380
return not has_errors
358381

359382

360383
def main():
361-
parser = argparse.ArgumentParser(description="Check if code format complies with clang-format standards, detect Chinese characters, and validate file headers")
384+
parser = argparse.ArgumentParser(
385+
description="Check if code format complies with clang-format standards, detect Chinese characters, and validate file headers",
386+
formatter_class=argparse.RawTextHelpFormatter
387+
)
388+
389+
# Mode selection
390+
parser.add_argument("--debug", "--local", action="store_true",
391+
help="Enable debug mode to check local specified files instead of PR files")
392+
393+
# PR mode options
362394
parser.add_argument("--base", default="master",
363395
help="Base branch name (default: master)")
396+
397+
# Debug mode options
398+
parser.add_argument("--files", nargs="+", metavar="FILE",
399+
help="Debug mode: Specify files to check (supports wildcards)")
400+
parser.add_argument("--dir", "--directories", nargs="+", metavar="DIR",
401+
help="Debug mode: Specify directories to check (recursive)")
402+
403+
# General options
364404
parser.add_argument("--verbose", "-v", action="store_true",
365405
help="Show verbose information")
366406

367407
args = parser.parse_args()
368408

409+
# Validation
410+
if args.debug and (args.files or args.dir):
411+
pass # Valid: debug mode with specified files/dirs
412+
elif args.debug:
413+
pass # Valid: debug mode scanning current directory
414+
elif not args.debug and (args.files or args.dir):
415+
print("Error: --files and --dir arguments can only be used in debug mode (--debug)")
416+
sys.exit(1)
417+
369418
if args.verbose:
370419
print(f"Project root: {Path.cwd()}")
371-
print(f"Base branch: {args.base}")
420+
if args.debug:
421+
print("Run mode: Debug mode")
422+
if args.files:
423+
print(f"Specified files: {args.files}")
424+
if args.dir:
425+
print(f"Specified directories: {args.dir}")
426+
else:
427+
print("Run mode: PR check mode")
428+
print(f"Base branch: {args.base}")
372429
print(f"Current year: {datetime.now().year}")
373430

374-
checker = FormatChecker(base_ref=args.base)
375-
success = checker.check_format()
431+
checker = FormatChecker(base_ref=args.base, debug_mode=args.debug)
432+
433+
if args.debug:
434+
success = checker.check_format(file_patterns=args.files, directories=args.dir)
435+
else:
436+
success = checker.check_format()
376437

377438
sys.exit(0 if success else 1)
378439

0 commit comments

Comments
 (0)