123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- import os
- import pathlib
- import re
- import subprocess
- import sys
- GOOD_LICENSE_HEADER_PATTERN = re.compile(
- '^/\\*\n' +
- '( \\* Copyright \\(c\\) [0-9]{4}(-[0-9]{4})?, .*\n)+' +
- ' \\*\n' +
- ' \\* SPDX-License-Identifier: BSD-2-Clause\n' +
- ' \\*/\n' +
- '\n')
- LICENSE_HEADER_CHECK_EXCLUDES = {
- 'AK/Checked.h',
- 'AK/Function.h',
- 'Libraries/LibCore/SocketpairWindows.cpp',
- }
- PRAGMA_ONCE_STRING = '#pragma once'
- GOOD_PRAGMA_ONCE_PATTERN = re.compile('(^|\\S\n\n)#pragma once(\n\n\\S.|$)')
- BAD_INCLUDE_LIBC = re.compile("# *include <LibC/")
- BAD_INCLUDE_COMPLEX = re.compile("# *include <c[c]?omplex")
- ANY_INCLUDE_PATTERN = re.compile('^ *# *include\\b.*[>"](?!\\)).*$', re.M)
- SYSTEM_INCLUDE_PATTERN = re.compile("^ *# *include *<([^>]+)>(?: /[*/].*)?$")
- LOCAL_INCLUDE_PATTERN = re.compile('^ *# *include *"([^>]+)"(?: /[*/].*)?$')
- INCLUDE_CHECK_EXCLUDES = {
- }
- LOCAL_INCLUDE_ROOT_OVERRIDES = {
- }
- LOCAL_INCLUDE_SUFFIX_EXCLUDES = [
-
-
- '.moc',
- ]
- SINGLE_PAGE_HTML_SPEC_LINK = re.compile('//.*https://html\\.spec\\.whatwg\\.org/#')
- def should_check_file(filename):
- if not filename.endswith('.cpp') and not filename.endswith('.h'):
- return False
- if filename.startswith('Base/'):
- return False
- if filename.startswith('Meta/CMake/vcpkg/overlay-ports/'):
- return False
- return True
- def find_files_here_or_argv():
- if len(sys.argv) > 1:
- raw_list = sys.argv[1:]
- else:
- process = subprocess.run(["git", "ls-files"], check=True, capture_output=True)
- raw_list = process.stdout.decode().strip('\n').split('\n')
- return filter(should_check_file, raw_list)
- def is_in_prefix_list(filename, prefix_list):
- return any(
- filename.startswith(prefix) for prefix in prefix_list
- )
- def find_matching_prefix(filename, prefix_list):
- matching_prefixes = [prefix for prefix in prefix_list if filename.startswith(prefix)]
- assert len(matching_prefixes) <= 1
- return matching_prefixes[0] if matching_prefixes else None
- def run():
- errors_license = []
- errors_pragma_once_bad = []
- errors_pragma_once_missing = []
- errors_include_libc = []
- errors_include_weird_format = []
- errors_include_missing_local = []
- errors_include_bad_complex = []
- errors_single_page_html_spec = []
- for filename in find_files_here_or_argv():
- with open(filename, mode="r", encoding='utf-8') as f:
- file_content = f.read()
- if not is_in_prefix_list(filename, LICENSE_HEADER_CHECK_EXCLUDES):
- if not GOOD_LICENSE_HEADER_PATTERN.search(file_content):
- errors_license.append(filename)
- if filename.endswith('.h'):
- if GOOD_PRAGMA_ONCE_PATTERN.search(file_content):
-
- pass
- elif PRAGMA_ONCE_STRING in file_content:
-
- errors_pragma_once_bad.append(filename)
- else:
-
- errors_pragma_once_missing.append(filename)
- if BAD_INCLUDE_LIBC.search(file_content):
- errors_include_libc.append(filename)
- if BAD_INCLUDE_COMPLEX.search(file_content):
- errors_include_bad_complex.append(filename)
- if not is_in_prefix_list(filename, INCLUDE_CHECK_EXCLUDES):
- if include_root := find_matching_prefix(filename, LOCAL_INCLUDE_ROOT_OVERRIDES):
- local_include_root = pathlib.Path(include_root)
- else:
- local_include_root = pathlib.Path(filename).parent
- for include_line in ANY_INCLUDE_PATTERN.findall(file_content):
- if SYSTEM_INCLUDE_PATTERN.match(include_line):
-
- continue
- local_match = LOCAL_INCLUDE_PATTERN.match(include_line)
- if local_match is None:
- print(f"Cannot parse include-line '{include_line}' in {filename}")
- if filename not in errors_include_weird_format:
- errors_include_weird_format.append(filename)
- continue
- relative_filename = local_match.group(1)
- referenced_file = local_include_root.joinpath(relative_filename)
- if referenced_file.suffix in LOCAL_INCLUDE_SUFFIX_EXCLUDES:
- continue
- if not referenced_file.exists():
- print(f"In {filename}: Cannot find {referenced_file}")
- if filename not in errors_include_missing_local:
- errors_include_missing_local.append(filename)
- if SINGLE_PAGE_HTML_SPEC_LINK.search(file_content):
- errors_single_page_html_spec.append(filename)
- have_errors = False
- if errors_license:
- print("Files with bad licenses:", " ".join(errors_license))
- have_errors = True
- if errors_pragma_once_missing:
- print("Files without #pragma once:", " ".join(errors_pragma_once_missing))
- have_errors = True
- if errors_pragma_once_bad:
- print("Files with a bad #pragma once:", " ".join(errors_pragma_once_bad))
- have_errors = True
- if errors_include_libc:
- print(
- "Files that include a LibC header using #include <LibC/...>:",
- " ".join(errors_include_libc),
- )
- have_errors = True
- if errors_include_weird_format:
- print(
- "Files that contain badly-formatted #include statements:",
- " ".join(errors_include_weird_format),
- )
- have_errors = True
- if errors_include_missing_local:
- print(
- "Files that #include a missing local file:",
- " ".join(errors_include_missing_local),
- )
- have_errors = True
- if errors_include_bad_complex:
- print(
- "Files that include a non-AK complex header:",
- " ".join(errors_include_bad_complex),
- )
- have_errors = True
- if errors_single_page_html_spec:
- print(
- "Files with links to the single-page HTML spec:",
- " ".join(errors_single_page_html_spec)
- )
- have_errors = True
- if have_errors:
- sys.exit(1)
- if __name__ == '__main__':
- os.chdir(os.path.dirname(__file__) + "/..")
- run()
|