From e29374fa0452018a740373af6035c46397038b8e Mon Sep 17 00:00:00 2001 From: Peter Barker Date: Wed, 10 Aug 2022 12:29:13 +1000 Subject: [PATCH] Tools: extract_features.py: allow for regular expressions for features Also switch to keying off the define in build_options.py rather than the label as the label is not unique and we'd have to munge them badly (and enforce shape) where we can just use the ArduPilot defines which are all pretty well-formed. --- Tools/scripts/extract_features.py | 62 ++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/Tools/scripts/extract_features.py b/Tools/scripts/extract_features.py index be78068e1f..393fc40899 100755 --- a/Tools/scripts/extract_features.py +++ b/Tools/scripts/extract_features.py @@ -13,6 +13,7 @@ import string import subprocess import sys import time +import build_options if sys.version_info[0] < 3: @@ -27,14 +28,19 @@ class ExtractFeatures(object): self.filename = filename self.nm = 'arm-none-eabi-nm' - # feature_name should match the equivalent feature in build_options.py - # ('FEATURE_NAME', 'EXPECTED_SYMBOL') + # feature_name should match the equivalent feature in + # build_options.py ('FEATURE_NAME', 'EXPECTED_SYMBOL'). + # EXPECTED_SYMBOL is a regular expression which will be matched + # against "define" in build_options's feature list, and + # FEATURE_NAME will have substitutions made from the match. + # the substitutions will be upper-cased self.features = [ - ('AIRSPEED', 'AP_Airspeed::AP_Airspeed',), - ('AP_ADSB', 'AP_ADSB::AP_ADSB',), - ('AP_EFI', 'AP_EFI::AP_EFI',), - ('BEACON', 'AP_Beacon::AP_Beacon',), - ('TORQEEDO', 'AP_Torqeedo::AP_Torqeedo'), + ('AP_AIRSPEED_ENABLED', 'AP_Airspeed::AP_Airspeed',), + ('AP_AIRSPEED_{type}_ENABLED', 'AP_Airspeed_(?P.*)::AP_Airspeed_(?P=type)',), + ('HAL_ADSB_ENABLED', 'AP_ADSB::AP_ADSB',), + ('HAL_EFI_ENABLED', 'AP_EFI::AP_EFI',), + ('BEACON_ENABLED', 'AP_Beacon::AP_Beacon',), + ('HAL_TORQEEDO_ENABLED', 'AP_Torqeedo::AP_Torqeedo'), ] def progress(self, string): @@ -96,13 +102,12 @@ class ExtractFeatures(object): else: self.symbols_without_arguments[extracted_symbol_name] = attributes - def find(self, name): - if '(' not in name: - # look for symbols without arguments - # print("Looking for (%s)" % str(name)) - return self.symbols_without_arguments[name] - - return self.symbols[name] + def dict_for_symbol(self, symbol): + if '(' not in symbol: + some_dict = self.symbols_without_arguments + else: + some_dict = self.symbols + return some_dict def extract_symbols_from_elf(self, filename): '''parses ELF in filename, returns dict of symbols=>attributes''' @@ -135,15 +140,30 @@ class ExtractFeatures(object): def run(self): - symbols = self.extract_symbols_from_elf(filename) + build_options_defines = set([x.define for x in build_options.BUILD_OPTIONS]) - for (feature_name, symbol) in self.features: - try: - symbols.find(symbol) - except KeyError: - continue + symbols = self.extract_symbols_from_elf(filename) - print("%s" % str(feature_name)) + for (feature_define, symbol) in self.features: + some_dict = symbols.dict_for_symbol(symbol) + # look for symbols without arguments + # print("Looking for (%s)" % str(name)) + for s in some_dict.keys(): + m = re.match(symbol, s) + # print("matching %s with %s" % (symbol, s)) + if m is None: + continue + d = m.groupdict() + for key in d.keys(): + d[key] = d[key].upper() + # filter to just the defines present in + # build_options.py - otherwise we end up with (e.g.) + # AP_AIRSPEED_BACKEND_ENABLED, even 'though that + # doesn't exist in the ArduPilot codebase. + some_define = feature_define.format(**d) + if some_define not in build_options_defines: + continue + print(some_define) if __name__ == '__main__':