diff --git a/tools/idf_py_actions/hints.yml b/tools/idf_py_actions/hints.yml index f03f2e0d0c..c599aad0b9 100644 --- a/tools/idf_py_actions/hints.yml +++ b/tools/idf_py_actions/hints.yml @@ -1,58 +1,76 @@ # - # re: Regular expression of error to search -# hint: Message of the hint. Optionally, it is possible to use '{}' at the place where the matched group from 're' should be inserted. This requires 'match_to_output: True'. +# hint: The message of the hint. Optionally, it is possible to use '{}' at the place where the matched group from 're' should be inserted. This requires 'match_to_output: True'. You can use variables with hint messages. For this, you need to add variables and "{}" in a place where you want to put your hint variable, but you can't use 'match_to_output' with variables. # match_to_output: (False by default) see the description of 'hint'. - - +# variables: +# - +# re_variables: [set variable for regular expression] +# hint_variables: [set variable for hint] # Rules to write regex for hints on how to resolve errors # - Do not use more than one whitespace in a row. The script automatically merges several whitespaces into one when capturing output # - Do not use \n in your regex. They are all automatically deletes by the script when capturing output +# +# example of using hints: +# - +# re: "Error: header {} is missing" (you can use '{1} ... {1}' placeholders in 'hint' and 're', so that you don't have to repeat the same variables, you can use 'hint: 'The {0} (functions/types/macros prefixed with '{1}') has been made into a private API. If users still require usage of the {0} (though this is not recommended), it can be included via #include "esp_private/{2}.h".' in this file as an example) +# hint: "header {} is missing, you need to add dependency on component {}" +# variables: +# - +# re_variables: [Q] +# hint_variables: [A, B] +# - +# re_variables: [W] +# hint_variables: [C, D] +# - +# re_variables: [R] +# hint_variables: [E, F] +# +# that example will replace this : +# - +# re: "Error: header Q is missing" +# hint: "header A is missing, you need to add dependency on component B" +# - +# re: Error: header W is missing" +# hint: "header C is missing, you need to add dependency on component D" +# - +# re: Error: header R is missing" +# hint: "header E is missing, you need to add dependency on component F" - re: "warning: passing argument 1 of 'esp_secure_boot_read_key_digests' from incompatible pointer type" hint: "The parameter type of the function esp_secure_boot_read_key_digests() has been changed from ets_secure_boot_key_digests_t* to esp_secure_boot_key_digests_t*." - - re: "error: implicit declaration of function 'bootloader_common_get_reset_reason'" - hint: "'bootloader_common_get_reset_reason()' has been removed. Please use the function 'esp_rom_get_reset_reason()' in the ROM component." + re: "error: implicit declaration of function '{}'" + hint: "Function '{}' has been removed. Please use the function {}." + variables: + - + re_variables: [bootloader_common_get_reset_reason] + hint_variables: [bootloader_common_get_reset_reason(), "'esp_rom_get_reset_reason()' in the ROM component"] + - + re_variables: [esp_efuse_get_chip_ver] + hint_variables: [esp_efuse_get_chip_ver(), efuse_hal_get_major_chip_version(), efuse_hal_get_minor_chip_version() or efuse_hal_chip_revision() instead] + - + re_variables: [(esp_spiram_get_chip_size|esp_spiram_get_size)] + hint_variables: [esp_spiram_get_chip_size and esp_spiram_get_size, esp_psram_get_size()] - re: "error: implicit declaration of function 'esp_secure_boot_verify_sbv2_signature_block|esp_secure_boot_verify_rsa_signature_block'" - hint: "'esp_secure_boot_verify_sbv2_signature_block()' and 'esp_secure_boot_verify_rsa_signature_block()' and has been made private and are no longer available." + hint: "'esp_secure_boot_verify_sbv2_signature_block()' and 'esp_secure_boot_verify_rsa_signature_block()' and have been made private and are no longer available." - - re: "error: implicit declaration of function 'esp_cpu_ccount_t'" - hint: "Use esp_cpu_cycle_count_t defined in esp_cpu.h instead of esp_cpu_ccount_t." - -- - re: "error: implicit declaration of function 'esp_cpu_(g|s)et_ccount'" - hint: "Use esp_cpu_{}et_cycle_count() defined in esp_cpu.h instead." - match_to_output: True - -- - re: "error: implicit declaration of function 'esp_efuse_get_chip_ver'" - hint: "Function esp_efuse_get_chip_ver() has been removed. Use efuse_hal_get_major_chip_version(), efuse_hal_get_minor_chip_version() or efuse_hal_chip_revision() instead." - -- - re: "error: implicit declaration of function '(esp_random|esp_fill_random)'" - hint: "esp_random.h header file is not included by esp_system.h anymore. It shall then be manually included with #include \"esp_random.h\"" - -- - re: "error: implicit declaration of function '(esp_base_mac_addr_(s|g)et|esp_efuse_mac_get_(custom|default)|esp_read_mac|esp_derive_local_mac)'" - hint: "esp_mac.h header file is not included by esp_system.h anymore. It shall then be manually included with #include \"esp_mac.h\"" - -- - re: "error: (implicit declaration of function 'esp_chip_info'|unknown type name 'esp_chip_info_t')" - hint: "esp_chip_info.h header file is not included by esp_system.h anymore. It shall then be manually included with #include \"esp_chip_info.h\"" - -- - re: "error: implicit declaration of function 'esp_int_wdt_\\w+'" - hint: "The Interrupt Watchdog API has been made private, it shall not be used anymore. You can still force its inclusion with #include \"esp_private/esp_int_wdt.h\" (not recommended)" - -- - re: "error: implicit declaration of function '(esp_spiram_get_chip_size|esp_spiram_get_size)'" - hint: "{}() has been deleted, please use esp_psram_get_size() instead." - match_to_output: True + re: "error: implicit declaration of function '{}'" + hint: '{0}.h header file is not included by esp_system.h anymore. It shall then be manually included with #include "{0}.h"' + variables: + - + re_variables: [(esp_random|esp_fill_random)] + hint_variables: [esp_random] + - + re_variables: [(esp_base_mac_addr_(s|g)et|esp_efuse_mac_get_(custom|default)|esp_read_mac|esp_derive_local_mac)] + hint_variables: [esp_mac] + - + re_variables: [esp_chip_info] + hint_variables: [esp_chip_info] - re: "fatal error: (spiram.h|esp_spiram.h): No such file or directory" @@ -60,19 +78,39 @@ match_to_output: True - - re: "fatal error: (soc/cpu.h|compare_set.h): No such file or directory" - hint: "{} was removed. Include and use the API function provided by esp_cpu.h instead." - match_to_output: True + re: "error: implicit declaration of function '{}'" + hint: "Use {} defined in esp_cpu.h instead of {}." + variables: + - + re_variables: [esp_cpu_ccount_t] + hint_variables: [esp_cpu_cycle_count_t, esp_cpu_ccount_t] + - + re_variables: [esp_cpu_get_ccount] + hint_variables: [esp_cpu_get_cycle_count(), esp_cpu_get_ccount] + - + re_variables: [esp_cpu_set_ccount] + hint_variables: [esp_cpu_set_cycle_count(), esp_cpu_set_ccount] - - re: "fatal error: (esp_intr.h): No such file or directory" - hint: "{} was removed. Include esp_intr_alloc.h instead." - match_to_output: True + re: "fatal error: {}: No such file or directory" + hint: "{} was removed. Include {} instead." + variables: + - + re_variables: [esp_intr.h] + hint_variables: [esp_intr.h, esp_intr_alloc.h] + - + re_variables: [soc/cpu.h] + hint_variables: [soc/cpu.h, and use the API function provided by esp_cpu.h] + - + re_variables: [compare_set.h] + hint_variables: [compare_set.h, and use the API function provided by esp_cpu.h] + - + re_variables: [esp_panic.h] + hint_variables: [esp_panic.h, use functionalities provided in esp_debug_helpers.h] - - re: "fatal error: (esp_panic.h): No such file or directory" - hint: "{} was made private. Use functionalities provided in esp_debug_helpers.h instead." - match_to_output: True + re: "error: implicit declaration of function 'esp_int_wdt_\\w+'" + hint: 'The Interrupt Watchdog API has been made private, it shall not be used anymore. You can still force its inclusion with #include "esp_private/esp_int_wdt.h" (not recommended)' - re: "fatal error: soc/(spinlock.h|clk_ctrl_os.h|rtc_wdt.h): No such file or directory" @@ -84,9 +122,6 @@ hint: "{} was renamed and made private. Consider using the logging APIs provided under esp_log.h instead." match_to_output: True -- - re: "fatal error: eh_frame_parser.h: No such file or directory" - hint: "Backtrace Parser API (eh_frame_parser.h) has been made private, it shall not be used anymore. You can still force its inclusion with #include \"esp_private/eh_frame_parser.h\" (not recommended)" - re: "error: unknown type name '(portTickType|xTaskHandle|xQueueHandle|xSemaphoreHandle|xQueueSetHandle|xQueueSetMemberHandle|xTimeOutType|xMemoryRegion|xTaskParameters|xTaskStatusType|xTimerHandle|xCoRoutineHandle|pdTASK_HOOK_CODE|tmrTIMER_CALLBACK|pdTASK_CODE|xListItem|xList)'" hint: "You maybe using pre FreeRTOS V8.0.0 data types. The backward compatibility of such data types is no longer enabled by default. Please turn on CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY explicitly to use such data types." @@ -117,19 +152,24 @@ match_to_output: True - - re: "fatal error: esp32\\w*\\/clk.h: No such file or directory" - hint: "The ESP Clock API (functions/types/macros prefixed with 'esp_clk') has been made into a private API. If users still require usage of the ESP Clock API (though this is not recommended), it can be included via '#include \"esp_private/esp_clk.h\"'." -- - re: "fatal error: esp32\\w*\\/cache_err_int.h: No such file or directory" - hint: "The Cache Error Interrupt API (functions/types/macros prefixed with 'esp_cache_err') has been made into a private API. If users still require usage of the Cache Error Interrupt API (though this is not recommended), it can be included via '#include \"esp_private/cache_err_int.h\"'" - -- - re: "fatal error: brownout.h: No such file or directory" - hint: "The Brownout API (functions/types/macros prefixed with 'esp_brownout') has been made into a private API. If users still require usage of the Brownout API (though this is not recommended), it can be included via '#include \"esp_private/brownout.h\"'." - -- - re: "fatal error: trax.h: No such file or directory" - hint: "The Trax API (functions/types/macros prefixed with 'trax_') has been made into a private API. If users still require usage of the Trax API (though this is not recommended), it can be included via '#include \"esp_private/trax.h\"'." + re: "fatal error: {}.h: No such file or directory" + hint: 'The {0} (functions/types/macros prefixed with "{1}") has been made into a private API. If users still require usage of the {0} (though this is not recommended), it can be included via #include "esp_private/{2}.h".' + variables: + - + re_variables: [esp32\\w*\\/clk] + hint_variables: [ESP Clock API, esp_clk, esp_clk] + - + re_variables: [esp32\\w*\\/cache_err_int] + hint_variables: [Cache Error Interrupt API, esp_cache_err, cache_err_int] + - + re_variables: [brownout] + hint_variables: [Brownout API, esp_brownout, brownout] + - + re_variables: [trax] + hint_variables: [Trax API, trax_, trax] + - + re_variables: [eh_frame_parser] + hint_variables: [Backtrace Parser API, eh_frame_parser, eh_frame_parser] - re: "fatal error: esp_adc_cal.h: No such file or directory" diff --git a/tools/idf_py_actions/tools.py b/tools/idf_py_actions/tools.py index f0646d0f9a..9a8e735aea 100644 --- a/tools/idf_py_actions/tools.py +++ b/tools/idf_py_actions/tools.py @@ -8,11 +8,10 @@ import sys from asyncio.subprocess import Process from io import open from types import FunctionType -from typing import Any, Dict, List, Optional, TextIO, Tuple, Union +from typing import Any, Dict, List, Match, Optional, TextIO, Tuple, Union import click import yaml -from idf_monitor_base.output_helpers import yellow_print from .constants import GENERATORS from .errors import FatalError @@ -79,6 +78,23 @@ def idf_version() -> Optional[str]: return version +def color_print(message: str, color: str, newline: Optional[str]='\n') -> None: + """ Print a message to stderr with colored highlighting """ + ansi_normal = '\033[0m' + sys.stderr.write('%s%s%s%s' % (color, message, ansi_normal, newline)) + sys.stderr.flush() + + +def yellow_print(message: str, newline: Optional[str]='\n') -> None: + ansi_yellow = '\033[0;33m' + color_print(message, ansi_yellow, newline) + + +def red_print(message: str, newline: Optional[str]='\n') -> None: + ansi_red = '\033[1;31m' + color_print(message, ansi_red, newline) + + def print_hints(*filenames: str) -> None: """Getting output files and printing hints on how to resolve errors based on the output.""" with open(os.path.join(os.path.dirname(__file__), 'hints.yml'), 'r') as file: @@ -87,18 +103,39 @@ def print_hints(*filenames: str) -> None: with open(file_name, 'r') as file: output = ' '.join(line.strip() for line in file if line.strip()) for hint in hints: + variables_list = hint.get('variables') + hint_list, hint_vars, re_vars = [], [], [] + match: Optional[Match[str]] = None try: - match = re.compile(hint['re']).findall(output) - except KeyError: - raise KeyError("Argument 're' missing in {}. Check hints.yml file.".format(hint)) + if variables_list: + for variables in variables_list: + hint_vars = variables['re_variables'] + re_vars = variables['hint_variables'] + regex = hint['re'].format(*re_vars) + if re.compile(regex).search(output): + try: + hint_list.append(hint['hint'].format(*hint_vars)) + except KeyError as e: + red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint)) + sys.exit(1) + else: + match = re.compile(hint['re']).search(output) + except KeyError as e: + red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint)) + sys.exit(1) except re.error as e: - raise re.error('{} from hints.yml have {} problem. Check hints.yml file.'.format(hint['re'], e)) - if match: - extra_info = ', '.join(match) if hint.get('match_to_output', '') else '' + red_print('{} from hints.yml have {} problem. Check hints.yml file.'.format(hint['re'], e)) + sys.exit(1) + if hint_list: + for message in hint_list: + yellow_print('HINT:', message) + elif match: + extra_info = ', '.join(match.groups()) if hint.get('match_to_output', '') else '' try: yellow_print(' '.join(['HINT:', hint['hint'].format(extra_info)])) - except KeyError: - raise KeyError("Argument 'hint' missing in {}. Check hints.yml file.".format(hint)) + except KeyError as e: + red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint)) + sys.exit(1) def fit_text_in_terminal(out: str) -> str: