From 5b59e53cc909925857ca1265588b0dd4ff222eb0 Mon Sep 17 00:00:00 2001 From: John Pennycook Date: Wed, 11 Sep 2024 11:21:36 +0100 Subject: [PATCH 1/4] Aggregate warnings to generate meta-warnings These meta-warnings are intended to direct the user's attention to cbi.log and provide some suggested actions. Signed-off-by: John Pennycook --- codebasin/__main__.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/codebasin/__main__.py b/codebasin/__main__.py index 87d3e68..23361f7 100755 --- a/codebasin/__main__.py +++ b/codebasin/__main__.py @@ -86,6 +86,22 @@ def format(self, record): return f"{BOLD}{color}{level}{RESET}: {msg}" +class WarningAggregator(logging.Filter): + """ + Inspect warnings to generate meta-warnings and statistics. + """ + + def __init__(self): + self.count = 0 + + def filter(self, record): + if record.levelno == logging.WARNING: + self.count += 1 + + # Do not filter anything. + return True + + def main(): # Read command-line arguments parser = argparse.ArgumentParser( @@ -181,11 +197,14 @@ def main(): # - All messages are written to a log file # - Only errors are written to the terminal by default # - Messages written to terminal are based on -q and -v flags + # - Meta-warnings and statistics are generated by a WarningAggregator + aggregator = WarningAggregator() log.setLevel(logging.DEBUG) file_handler = logging.FileHandler("cbi.log", mode="w") file_handler.setLevel(logging.INFO) file_handler.setFormatter(Formatter()) + file_handler.addFilter(aggregator) log.addHandler(file_handler) # Inform the user that a log file has been created. @@ -257,6 +276,16 @@ def main(): legacy_warnings=False, ) + # Generate meta-warnings and statistics. + # Temporarily override log_level to ensure they are visible. + if aggregator.count > 0: + stdout_handler.setLevel(logging.WARNING) + log.warning( + f"{aggregator.count} warnings generated during preprocessing." + + " See cbi.log for details.", + ) + stdout_handler.setLevel(log_level) + # Count lines for platforms platform_mapper = PlatformMapper(codebase) setmap = platform_mapper.walk(state) From c50c65fb97296391c37f5ad1755ff909c0ba3826 Mon Sep 17 00:00:00 2001 From: John Pennycook Date: Thu, 19 Sep 2024 15:54:15 +0100 Subject: [PATCH 2/4] Refactor meta-warnings using a dedicated class Simplifies the process of defining multiple meta-warnings. Signed-off-by: John Pennycook --- codebasin/__main__.py | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/codebasin/__main__.py b/codebasin/__main__.py index 23361f7..60fd5ef 100755 --- a/codebasin/__main__.py +++ b/codebasin/__main__.py @@ -8,6 +8,7 @@ import argparse import logging import os +import re import sys from codebasin import CodeBase, config, finder, report, util @@ -86,21 +87,49 @@ def format(self, record): return f"{BOLD}{color}{level}{RESET}: {msg}" +class MetaWarning: + """ + A MetaWarning is used to represent multiple warnings, and provide suggested + actions to the user. + """ + + def __init__(self, regex: str, msg: str): + self.regex = re.compile(regex) + self.msg = msg + self._count = 0 + + def inspect(self, record): + if self.regex.match(record.msg): + self._count += 1 + + def warn(self): + if self._count == 0: + return + log.warning(self.msg.format(self._count)) + + class WarningAggregator(logging.Filter): """ Inspect warnings to generate meta-warnings and statistics. """ def __init__(self): - self.count = 0 + self.meta_warnings = [ + MetaWarning(".*", "{} warnings generated during preprocessing."), + ] def filter(self, record): if record.levelno == logging.WARNING: - self.count += 1 + for meta_warning in self.meta_warnings: + meta_warning.inspect(record) # Do not filter anything. return True + def warn(self): + for meta_warning in self.meta_warnings: + meta_warning.warn() + def main(): # Read command-line arguments @@ -278,13 +307,9 @@ def main(): # Generate meta-warnings and statistics. # Temporarily override log_level to ensure they are visible. - if aggregator.count > 0: - stdout_handler.setLevel(logging.WARNING) - log.warning( - f"{aggregator.count} warnings generated during preprocessing." - + " See cbi.log for details.", - ) - stdout_handler.setLevel(log_level) + stdout_handler.setLevel(logging.WARNING) + aggregator.warn() + stdout_handler.setLevel(log_level) # Count lines for platforms platform_mapper = PlatformMapper(codebase) From 78d472a1d01d2523f544f1493d60a4a9f8da8dd9 Mon Sep 17 00:00:00 2001 From: John Pennycook Date: Fri, 20 Sep 2024 10:55:13 +0100 Subject: [PATCH 3/4] Add meta-warnings for missing include files Provide suggested solutions for fixing each type of warning. In future, we may want to add an option to silence these warnings altogether if the user knows that they are safe to ignore. Signed-off-by: John Pennycook --- codebasin/__main__.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/codebasin/__main__.py b/codebasin/__main__.py index 60fd5ef..98a154f 100755 --- a/codebasin/__main__.py +++ b/codebasin/__main__.py @@ -99,7 +99,7 @@ def __init__(self, regex: str, msg: str): self._count = 0 def inspect(self, record): - if self.regex.match(record.msg): + if self.regex.search(record.msg): self._count += 1 def warn(self): @@ -115,7 +115,25 @@ class WarningAggregator(logging.Filter): def __init__(self): self.meta_warnings = [ - MetaWarning(".*", "{} warnings generated during preprocessing."), + MetaWarning(".", "{} warnings generated during preprocessing."), + MetaWarning( + "user include", + "{} user include files could not be found.\n" + + " These could contain important macros and includes.\n" + + " Suggested solutions:\n" + + " - Check that the file(s) exist in the code base.\n" + + " - Check the include paths in the compilation database.\n" + + " - Check if the include(s) should have used '<>'.", + ), + MetaWarning( + "system include", + "{} system include files could not be found.\n" + + " These could define important feature macros.\n" + + " Suggested solutions:\n" + + " - Check that the file(s) exist on your system.\n" + + " - Use .cbi/config to define system include paths.\n" + + " - Use .cbi/config to define important macros.", + ), ] def filter(self, record): From 9abcf0c545542fe2bdb853bea0c49267b4314e8a Mon Sep 17 00:00:00 2001 From: John Pennycook Date: Thu, 10 Oct 2024 11:07:50 +0100 Subject: [PATCH 4/4] Fix type annotations for logging classes Signed-off-by: John Pennycook --- codebasin/__main__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/codebasin/__main__.py b/codebasin/__main__.py index 98a154f..3afa1a3 100755 --- a/codebasin/__main__.py +++ b/codebasin/__main__.py @@ -56,10 +56,10 @@ def _help_string(*lines: str, is_long=False, is_last=False): class Formatter(logging.Formatter): - def __init__(self, *, colors=False): + def __init__(self, *, colors: bool = False): self.colors = colors - def format(self, record): + def format(self, record: logging.LogRecord) -> str: msg = record.msg level = record.levelname.lower() @@ -98,7 +98,7 @@ def __init__(self, regex: str, msg: str): self.msg = msg self._count = 0 - def inspect(self, record): + def inspect(self, record: logging.LogRecord): if self.regex.search(record.msg): self._count += 1 @@ -136,7 +136,7 @@ def __init__(self): ), ] - def filter(self, record): + def filter(self, record: logging.LogRecord) -> bool: if record.levelno == logging.WARNING: for meta_warning in self.meta_warnings: meta_warning.inspect(record)