| 1 | import glob |
| 2 | import sys |
| 3 | import re |
| 4 | import os.path |
| 5 | |
| 6 | EXPECTED_HEADER = '.*Copyright \\(c\\) Microsoft.*All rights reserved.*'.casefold() |
| 7 | EXTENSIONS = ['.c', '.cpp', '.cxx', '.h', '.hpp', '.hxx'] |
| 8 | |
| 9 | def has_header(path: str) -> bool: |
| 10 | with open(path, 'rb') as fd: |
| 11 | lines = fd.read().decode('utf-8', 'ignore').replace('\r', '').split('\n') |
| 12 | |
| 13 | in_multiline_comment = False |
| 14 | |
| 15 | for e in lines[:50]: |
| 16 | if e.startswith('/*'): # Simplified comment parsing |
| 17 | in_multiline_comment = True |
| 18 | |
| 19 | if '*/' in e: |
| 20 | in_multiline_comment = False |
| 21 | |
| 22 | if e.strip().startswith('//') or in_multiline_comment: |
| 23 | if re.match(EXPECTED_HEADER, e.casefold()): |
| 24 | return True |
| 25 | |
| 26 | |
| 27 | return False |
| 28 | |
| 29 | def is_source_file(path: str) -> bool: |
| 30 | return any(e for e in EXTENSIONS if path.casefold().endswith(e)) |
| 31 | |
| 32 | def generate_header(path: str): |
| 33 | with open(path, 'rb') as fd: |
| 34 | content = fd.read().decode('utf-8', 'ignore') |
| 35 | |
| 36 | header = f'''/*++ |
| 37 | |
| 38 | Copyright (c) Microsoft. All rights reserved. |
| 39 | |
| 40 | Module Name: |
| 41 | |
| 42 | {os.path.basename(path)} |
| 43 | |
| 44 | Abstract: |
| 45 | |
| 46 | TODO |
| 47 | |
| 48 | --*/ |
| 49 | '''.replace('\n', '\r\n') |
| 50 | |
| 51 | with open(path, 'wb') as fd: |
| 52 | fd.write((header + content).encode('utf-8')) |
| 53 | |
| 54 | |
| 55 | def main(path: str, fix: bool): |
| 56 | files = glob.glob(f'{path}/**', recursive=True) |
| 57 | |
| 58 | source_files = [e for e in files if is_source_file(e)] |
| 59 | print(f'Validate copyright headers for {len(source_files)} files') |
| 60 | |
| 61 | missing_headers = [e for e in source_files if not has_header(e)] |
| 62 | |
| 63 | if missing_headers: |
| 64 | if fix: |
| 65 | for e in missing_headers: |
| 66 | generate_header(e) |
| 67 | |
| 68 | files = "\n".join(missing_headers) |
| 69 | print(f'{len(missing_headers)} files are missing a copyright header:\n{files}') |
| 70 | sys.exit(1) |
| 71 | |
| 72 | |
| 73 | if __name__ == '__main__': |
| 74 | path = '.' |
| 75 | fix = False |
| 76 | |
| 77 | for e in sys.argv[1:]: |
| 78 | if e == '--fix': |
| 79 | fix = True |
| 80 | else: |
| 81 | path = e |
| 82 | |
| 83 | main(path, fix) |