#!/usr/bin/env python3 # -*- mode: python-mode; python-indent-offset: 4; -*- # SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: © 2022 Stanislas Daniel Claude Dolcini import argparse from os import listdir, path from logging import getLogger, StreamHandler, INFO, WARNING, Formatter, Filter from pathlib import Path from sys import stdout, stderr class SingleLevelFilter(Filter): def __init__(self, passlevel, reject): self.passlevel = passlevel self.reject = reject def filter(self, record): if self.reject: return (record.levelno != self.passlevel) else: return (record.levelno == self.passlevel) class MissingMacroHeaderFinder(): def __init__(self, root, macro = None, include = None, verbose=False): self.root = Path(root) self.verbose = verbose self.include = include self.macro = macro self.__init_logger self.bad_files = set() @property def __init_logger(self): logger = getLogger(__name__) logformat = '%(levelname)s - %(message)s' logger.setLevel(INFO) # create a console handler, seems nicer to Windows and for future uses ch = StreamHandler(stdout) ch.setLevel(INFO) ch.setFormatter(Formatter(logformat)) f1 = SingleLevelFilter(INFO, False) ch.addFilter(f1) logger.addHandler(ch) errorch = StreamHandler(stderr) errorch.setLevel(WARNING) errorch.setFormatter(Formatter(logformat)) logger.addHandler(errorch) self.logger = logger def find_files_recursively(self, folder_path, extension): files = [] for file_name in listdir(folder_path): file_path = path.join(folder_path, file_name) if path.isdir(file_path): for file in self.find_files_recursively(file_path, extension): files.append(file) elif extension in file_name: files.append(file_path) return files; def get_bad_source_files(self): source_files = self.find_files_recursively(self.root, '.cpp'); if(self.verbose): self.logger.info(f"Found {len(source_files)} source file{'s' if len(source_files) > 0 else ''} to parse.") for source_file in source_files: with open(source_file, encoding = "utf-8") as source_file_io_wrapper: file_data = source_file_io_wrapper.read() if self.macro in file_data and not self.include in file_data: name, _ = path.splitext(source_file) header_file = f"{name}.h" if not path.exists(header_file): if(self.verbose): self.logger.warning(f"Could not find {header_file}") self.bad_files.add(source_file) continue with open(header_file) as header_file_io_wrapper: header_file_data = header_file_io_wrapper.read() if self.include not in header_file_data: self.bad_files.add(source_file) def get_bad_headers(self): header_files = self.find_files_recursively(self.root, '.h'); if(self.verbose): self.logger.info(f"Found {len(header_files)} header file{'s' if len(header_files) > 0 else ''} to parse.") for header_file in header_files: with open(header_file, encoding = "utf-8") as header_file_io_wrapper: file_data = header_file_io_wrapper.read() if self.macro in file_data and not self.include in file_data: self.bad_files.add(header_file) def run(self): if(self.verbose): self.logger.info(f"Parsing {self.root}") self.get_bad_source_files() self.get_bad_headers() if(self.verbose): self.logger.info(f"Found {len(self.bad_files)} file{'s' if len(self.bad_files) > 0 else ''} with missing headers for {self.macro}.") for file_path in self.bad_files: self.logger.error(file_path) if __name__ == '__main__': default_root = path.dirname(path.realpath(__file__)) parser = argparse.ArgumentParser(description='Missing Macro Header Finder.') parser.add_argument('-m', '--macro', action='store', dest='macro', default='CONFIG2_GLES') parser.add_argument('-i', '--include', action='store', dest='include', default='#include "lib/config2.h"') parser.add_argument('-r', '--root', action='store', dest='root', default=default_root) parser.add_argument('-v', '--verbose', action='store_true', default=False, help="Log validation errors.") args = parser.parse_args() finder = MissingMacroHeaderFinder(args.root, args.macro, args.include, args.verbose) finder.run()