1#!/usr/bin/env python 2# coding=utf-8 3# 4# CI script to check build logs for warnings. 5# Reads the list of builds, in the format produced by find_apps.py or build_apps.py, and finds warnings in the 6# log files for every build. 7# Exits with a non-zero exit code if any warning is found. 8 9import argparse 10import logging 11import os 12import re 13import sys 14 15try: 16 from find_build_apps import BuildItem, setup_logging 17except ImportError: 18 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) 19 from find_build_apps import BuildItem, setup_logging 20 21WARNING_REGEX = re.compile(r'(?:error|warning)[^\w]', re.MULTILINE | re.IGNORECASE) 22 23IGNORE_WARNS = [ 24 re.compile(r_str) for r_str in [ 25 r'library/error\.o', 26 r'/.*error\S*\.o', 27 r'.*error.*\.c\.obj', 28 r'.*error.*\.cpp\.obj', 29 r'.*error.*\.cxx\.obj', 30 r'.*error.*\.cc\.obj', 31 r'-Werror', 32 r'error\.d', 33 r'/.*error\S*.d', 34 r'reassigning to symbol', 35 r'changes choice state', 36 r'crosstool_version_check\.cmake', 37 r'CryptographyDeprecationWarning', 38 r'Warning: \d+/\d+ app partitions are too small for binary' 39 ] 40] 41 42 43def line_has_warnings(line): # type: (str) -> bool 44 if not WARNING_REGEX.search(line): 45 return False 46 47 has_warnings = True 48 for ignored in IGNORE_WARNS: 49 if re.search(ignored, line): 50 has_warnings = False 51 break 52 53 return has_warnings 54 55 56def main(): # type: () -> None 57 parser = argparse.ArgumentParser(description='ESP-IDF app builder') 58 parser.add_argument( 59 '-v', 60 '--verbose', 61 action='count', 62 help='Increase the logging level of the script. Can be specified multiple times.', 63 ) 64 parser.add_argument( 65 '--log-file', 66 type=argparse.FileType('w'), 67 help='Write the script log to the specified file, instead of stderr', 68 ) 69 parser.add_argument( 70 'build_list', 71 type=argparse.FileType('r'), 72 nargs='?', 73 default=sys.stdin, 74 help='Name of the file to read the list of builds from. If not specified, read from stdin.', 75 ) 76 args = parser.parse_args() 77 setup_logging(args) 78 79 build_items = [BuildItem.from_json(line) for line in args.build_list] 80 if not build_items: 81 logging.warning('Empty build list') 82 SystemExit(0) 83 84 found_warnings = 0 85 for build_item in build_items: 86 if not build_item.build_log_path: 87 logging.debug('No log file for {}'.format(build_item.work_dir)) 88 continue 89 with open(build_item.build_log_path, 'r') as log_file: 90 for line_no, line in enumerate(log_file): 91 if line_has_warnings(line): 92 logging.error('Issue in app {}, config {}:'.format(build_item.app_dir, build_item.config_name)) 93 logging.error(line.rstrip('\n')) 94 logging.error('See {}:{} for details'.format(os.path.basename(build_item.build_log_path), 95 line_no + 1)) 96 found_warnings += 1 97 break 98 99 if found_warnings: 100 logging.error('Checked {} builds, found {} warnings'.format(len(build_items), found_warnings)) 101 raise SystemExit(1) 102 103 logging.info('No warnings found') 104 105 106if __name__ == '__main__': 107 main() 108