blob: dce4a266f5ccdfd6ca930e4923c2b2abf2b2f69a [file] [log] [blame] [raw]
# -*- 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)