#!/usr/bin/env python3 # # Copyright (C) 2018-2021 Intel Corporation # # SPDX-License-Identifier: MIT # """Usage: ./scripts/lint/set_copyright.py """ import re import sys import os import datetime import stat import argparse def is_banned(path): """Check if path is banned.""" banned_paths = [ 'scripts/tests/copyright/in', 'scripts/tests/copyright/out', 'third_party' ] banned_files = [ 'scripts/lint/set_copyright.sh' ] path_banned = False for banned_file in banned_files: if os.path.normpath(path) == os.path.normpath(banned_file): path_banned = True break if not path_banned: dirname = os.path.dirname(path) for banned_path in banned_paths: if dirname.startswith(banned_path): path_banned = True break return path_banned def can_be_scanned(path): """Check whether we should scan this file""" allowed_extensions = [ 'cpp', 'h', 'inl', 'hpp', 'm', 'cmake', 'py', 'sh', 'cl', 'exports' ] allowed_extensions_2 = [ 'h.in', 'rc.in', 'options.txt' ] allowed_files = [ 'CMakeLists.txt' ] path_ext = path.split('.') path_ok = False filename = os.path.basename(path) if not os.path.isfile(path): print(f'Cannot find file {path}, skipping') path_ok = False elif is_banned(path): path_ok = False elif filename in allowed_files: path_ok = True elif path_ext[-1].lower() in allowed_extensions: path_ok = True elif '.'.join(path_ext[-2:]) in allowed_extensions_2: path_ok = True if not path_ok: print(f'[MIT] Ignoring file: {path}') return path_ok def _parse_args(): parser = argparse.ArgumentParser(description='Usage: ./scripts/lint/set_copyright.py ') parser.add_argument('-c', '--check', action='store_true', help='Checks only, not changing files, fails if wrong copyright') parser.add_argument('files', nargs='*') args = parser.parse_args() return vars(args) def main(args): header_cpp = """/* * Copyright (C) {} Intel Corporation * * SPDX-License-Identifier: MIT * */ """ header_bash_style = """# # Copyright (C) {} Intel Corporation # # SPDX-License-Identifier: MIT # """ cpp_sharp_lines = [ '#pragma', '#include' ] status = 0 for path in args['files']: # avoid self scan if os.path.abspath(path) == os.path.abspath(sys.argv[0]): continue if not can_be_scanned(path): continue print(f'[MIT] Processing file: {path}') gathered_lines = [] gathered_header = [] start_year = None header = header_cpp header_start = '/*' header_end = '*/' comment_char = r'\*' # now read line by line with open(path) as fin: # take care of hashbang first_line = fin.readline() if not first_line.startswith('#!'): line = first_line first_line = '' else: line = fin.readline() while(line == '\n'): line = fin.readline() is_cpp = False # check whether comment type is '#' if first_line or line.startswith('#'): for i in cpp_sharp_lines: print(f'a: {i} ~ {line}') if line.startswith(i): is_cpp = True break if not is_cpp: header_start = '#' header_end = '\n' header = header_bash_style comment_char = '#' curr_comment = [] is_header = None is_header_end = None # copyright have to be first comment in file if line.startswith(header_start): is_header = True is_header_end = False else: is_header = False is_header_end = True is_copyright = False while line: if is_header: if header_end == '\n' and len(line.strip()) == 0: is_header = False is_header_end = True elif line.strip().endswith(header_end): is_header = False is_header_end = True elif 'Copyright' in line: expr = (rf'^{comment_char} Copyright \([Cc]\) (\d+)( *- *\d+)?') match = re.match(expr, line.strip()) if match: start_year = match.groups()[0] curr_comment = [] is_copyright = True if not is_copyright: curr_comment.append(line) gathered_header.append(line) elif is_copyright and is_header_end: if len(line.strip()) > 0: gathered_lines.append(line) is_header_end = False else: gathered_header.append(line) else: gathered_lines.append(line) line = fin.readline() year = datetime.datetime.now().year if start_year is None: start_year = str(year) elif int(start_year) < year: start_year += '-' start_year += str(year) written_header = [header.format(start_year)] if len(curr_comment) > 0 or len(gathered_lines) > 0: written_header.append('\n') if len(curr_comment) > 0: written_header.append(''.join(curr_comment)) if not args['check']: # store file mode because we want to preserve this old_mode = os.stat(path)[stat.ST_MODE] os.remove(path) with open(path, 'w') as fout: if first_line: fout.write(f'{first_line}\n') fout.write(''.join(written_header)) contents = ''.join(gathered_lines) fout.write(contents) # chmod to original value os.chmod(path, old_mode) if args['check'] and ''.join(gathered_header) != ''.join(written_header): _tmp = [] for _itm in written_header: _tmp.extend(f'{_aa}\n' for _aa in _itm.split('\n')) print('--- source') print('+++ updated') print('@@') for idx, shl in enumerate(gathered_header): if idx>=len(_tmp): print('-%s' % shl.strip()) elif shl != _tmp[idx]: print('-%s' % shl.strip()) print('+%s' % _tmp[idx].strip()) else: print(' %s' % shl.strip()) status = 1 return status if __name__ == '__main__': sys.exit(main(_parse_args()))