| # -*- coding: utf-8 -*- |
| # Copyright (c) 2022, Compiler Explorer Authors |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are met: |
| # |
| # * Redistributions of source code must retain the above copyright notice, |
| # this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| # POSSIBILITY OF SUCH DAMAGE. |
| import sys |
| from os import listdir |
| from os.path import isfile, join |
| import re |
| |
| COMPILERS_LIST_RE = re.compile(r'compilers=(.*)') |
| ALIAS_LIST_RE = re.compile(r'alias=(.*)') |
| GROUP_NAME_RE = re.compile(r'group\.(.*?)\.') |
| COMPILER_EXE_RE = re.compile(r'compiler\.(.*?)\.exe') |
| DEFAULT_COMPILER_RE = re.compile(r'defaultCompiler=(.*)') |
| FORMATTERS_LIST_RE = re.compile(r'formatters=(.*)') |
| FORMATTER_EXE_RE = re.compile(r'formatter\.(.*?)\.exe') |
| LIBS_LIST_RE = re.compile(r'libs=(.+)') |
| LIB_VERSIONS_LIST_RE = re.compile(r'libs\.(.*?)\.versions=(.*)') |
| LIB_VERSION_RE = re.compile(r'libs\.(.*?)\.versions\.(.*?)\.version') |
| TOOLS_LIST_RE = re.compile(r'tools=(.+)') |
| TOOL_EXE_RE = re.compile(r'tools\.(.*?)\.exe') |
| EMPTY_LIST_RE = re.compile(r'.*(compilers|formatters|versions|tools|alias|exclude)=.*::.*') |
| DISABLED_RE = re.compile(r'^# Disabled:\s*(.*)') |
| |
| |
| def match_and_add(line, expr, s): |
| match = expr.match(line) |
| if match: |
| s.add(match.group(1)) |
| return match |
| |
| |
| def match_and_update(line, expr, s, split=':'): |
| match = expr.match(line) |
| if match: |
| s.update(match.group(1).split(split)) |
| return match |
| |
| |
| def process_file(file: str): |
| default_compiler = set() |
| listed_groups = set() |
| seen_groups = set() |
| listed_compilers = set() |
| seen_compilers = set() |
| listed_formatters = set() |
| seen_formatters = set() |
| listed_tools = set() |
| seen_tools = set() |
| listed_libs_ids = set() |
| seen_libs_ids = set() |
| listed_libs_versions = set() |
| seen_libs_versions = set() |
| empty_separators = set() |
| disabled = set() |
| with open(file) as f: |
| for line in f: |
| match_empty = EMPTY_LIST_RE.match(line) |
| if match_empty: |
| empty_separators.add(f"{line}") |
| |
| match_compilers = COMPILERS_LIST_RE.search(line) |
| if match_compilers: |
| ids = match_compilers.group(1).split(':') |
| for elem_id in ids: |
| if elem_id.startswith('&'): |
| listed_groups.add(elem_id[1:]) |
| elif '@' not in elem_id: |
| listed_compilers.add(elem_id) |
| |
| match_libs_versions = LIB_VERSIONS_LIST_RE.match(line) |
| if match_libs_versions: |
| lib_id = match_libs_versions.group(1) |
| versions = match_libs_versions.group(2).split(':') |
| seen_libs_ids.add(lib_id) |
| listed_libs_versions.update([f"{lib_id} {v}" for v in versions]) |
| |
| match_libs_version = LIB_VERSION_RE.match(line) |
| if match_libs_version: |
| lib_id = match_libs_version.group(1) |
| version = match_libs_version.group(2) |
| seen_libs_versions.add(f"{lib_id} {version}") |
| |
| if( |
| match_and_add(line, DEFAULT_COMPILER_RE, default_compiler) or |
| match_and_add(line, GROUP_NAME_RE, seen_groups) or |
| match_and_add(line, COMPILER_EXE_RE, seen_compilers) or |
| match_and_add(line, FORMATTER_EXE_RE, seen_formatters) or |
| match_and_add(line, TOOL_EXE_RE, seen_tools) or |
| match_and_update(line, ALIAS_LIST_RE, seen_compilers) or |
| match_and_update(line, FORMATTERS_LIST_RE, listed_formatters) or |
| match_and_update(line, TOOLS_LIST_RE, listed_tools) or |
| match_and_update(line, LIBS_LIST_RE, listed_libs_ids) or |
| match_and_update(line, DISABLED_RE, disabled, ' ') |
| ): |
| continue |
| bad_compilers = listed_compilers.symmetric_difference(seen_compilers) |
| bad_groups = listed_groups.symmetric_difference(seen_groups) |
| bad_formatters = listed_formatters.symmetric_difference(seen_formatters) |
| bad_libs_ids = listed_libs_ids.symmetric_difference(seen_libs_ids) |
| bad_libs_versions = listed_libs_versions.symmetric_difference(seen_libs_versions) |
| bad_tools = listed_tools.symmetric_difference(seen_tools) |
| bad_default = default_compiler - listed_compilers |
| return (file, |
| bad_compilers - disabled, |
| bad_groups - disabled, |
| bad_formatters - disabled, |
| bad_libs_ids - disabled, |
| bad_libs_versions - disabled, |
| bad_tools - disabled, |
| bad_default, |
| empty_separators) |
| |
| |
| def process_folder(folder: str): |
| return [process_file(join(folder, f)) |
| for f in listdir(folder) |
| if isfile(join(folder, f)) |
| and not (f.endswith('.defaults.properties') or f.endswith('.local.properties')) |
| and f.endswith('.properties')] |
| |
| |
| def problems_found(file_result): |
| return any([len(r) for r in file_result[1:]]) |
| |
| |
| def print_issue(name, result): |
| if len(result) > 0: |
| sep = "\n\t" |
| print(f"{name}:\n\t{sep.join(sorted(result))}") |
| |
| |
| def find_orphans(folder: str): |
| result = sorted([r for r in process_folder(folder) if problems_found(r)], key=lambda x: x[0]) |
| if result: |
| print(f"Found {len(result)} property file(s) with mismatching ids:") |
| for r in result: |
| print(r[0]) |
| print_issue("COMPILERS", r[1]) |
| print_issue("GROUPS", r[2]) |
| print_issue("FORMATTERS", r[3]) |
| print_issue("LIB IDS", r[4]) |
| print_issue("LIB VERSIONS", r[5]) |
| print_issue("TOOLS", r[6]) |
| print_issue("UNKNOWN DEFAULT COMPILER", r[7]) |
| print_issue("EMPTY LISTINGS", r[8]) |
| print("") |
| print("To suppress this warning on IDs that are temporally disabled, " |
| "add one or more comments to each listed file:") |
| print("# Disabled: id1 id2 ...") |
| else: |
| print("No configuration mismatches found") |
| return result |
| |
| |
| if __name__ == '__main__': |
| if find_orphans('./etc/config/'): |
| sys.exit(1) |