This PR updates two dependency libraries to their latest versions: ## espressif/button: v3.5.0 to v4.1.5 [Version 4](https://components.espressif.com/components/espressif/button/versions/4.1.5/changelog?language=en) changed the API. This code makes use of the new API, with no change to the existing behavior. ## chmorgan/esp-audio-player: v1.0.7 to v1.1.0 [Version 1.1.0](https://github.com/chmorgan/esp-audio-player/releases/tag/v1.1.0) introduces the possibility of multiple simultaneous audio streams. This feature is as yet unused by KTag. Co-authored-by: Joe Kearney <joe@clubk.club> Reviewed-on: #16
238 lines
7.2 KiB
Python
238 lines
7.2 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import argparse
|
|
import csv
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import re
|
|
from io import StringIO
|
|
|
|
OPT_MIN_LEN = 7
|
|
|
|
espidf_objdump = None
|
|
espidf_missing_function_info = True
|
|
|
|
class sdkconfig_c:
|
|
def __init__(self, path):
|
|
with open(path) as f:
|
|
lines = f.read().splitlines()
|
|
config = {}
|
|
for l in lines:
|
|
if len(l) > OPT_MIN_LEN and l[0] != '#':
|
|
mo = re.match(r'(.*)=(.*)', l, re.M | re.I)
|
|
if mo:
|
|
config[mo.group(1)] = mo.group(2).replace('"', '')
|
|
self.config = config
|
|
|
|
def index(self, key):
|
|
return self.config[key]
|
|
|
|
def check(self, options):
|
|
options = options.replace(' ', '')
|
|
if '&&' in options:
|
|
for i in options.split('&&'):
|
|
if i[0] == '!':
|
|
i = i[1:]
|
|
if i in self.config:
|
|
return False
|
|
else:
|
|
if i not in self.config:
|
|
return False
|
|
else:
|
|
i = options
|
|
if i[0] == '!':
|
|
i = i[1:]
|
|
if i in self.config:
|
|
return False
|
|
else:
|
|
if i not in self.config:
|
|
return False
|
|
return True
|
|
|
|
class object_c:
|
|
def read_dump_info(self, paths):
|
|
new_env = os.environ.copy()
|
|
new_env['LC_ALL'] = 'C'
|
|
dumps = []
|
|
print('paths:', paths)
|
|
for path in paths:
|
|
try:
|
|
dump_output = subprocess.check_output([espidf_objdump, '-t', path], env=new_env).decode()
|
|
dumps.append(StringIO(dump_output).readlines())
|
|
except subprocess.CalledProcessError as e:
|
|
raise RuntimeError(f"Command '{e.cmd}' failed with exit code {e.returncode}")
|
|
return dumps
|
|
|
|
def get_func_section(self, dumps, func):
|
|
for dump in dumps:
|
|
for line in dump:
|
|
if f' {func}' in line and '*UND*' not in line:
|
|
m = re.match(r'(\S*)\s*([glw])\s*([F|O])\s*(\S*)\s*(\S*)\s*(\S*)\s*', line, re.M | re.I)
|
|
if m and m[6] == func:
|
|
return m.group(4).replace('.text.', '')
|
|
if espidf_missing_function_info:
|
|
print(f'{func} failed to find section')
|
|
return None
|
|
else:
|
|
raise RuntimeError(f'{func} failed to find section')
|
|
|
|
def __init__(self, name, paths, library):
|
|
self.name = name
|
|
self.library = library
|
|
self.funcs = {}
|
|
self.paths = paths
|
|
self.dumps = self.read_dump_info(paths)
|
|
self.section_all = False
|
|
|
|
def append(self, func):
|
|
section = None
|
|
|
|
if func == '.text.*':
|
|
section = '.literal .literal.* .text .text.*'
|
|
self.section_all = True
|
|
elif func == '.iram1.*':
|
|
section = '.iram1 .iram1.*'
|
|
self.section_all = True
|
|
elif func == '.wifi0iram.*':
|
|
section = '.wifi0iram .wifi0iram.*'
|
|
self.section_all = True
|
|
elif func == '.wifirxiram.*':
|
|
section = '.wifirxiram .wifirxiram.*'
|
|
self.section_all = True
|
|
else:
|
|
section = self.get_func_section(self.dumps, func)
|
|
|
|
if section is not None:
|
|
self.funcs[func] = section
|
|
|
|
def functions(self):
|
|
nlist = []
|
|
for i in self.funcs:
|
|
nlist.append(i)
|
|
return nlist
|
|
|
|
def sections(self):
|
|
nlist = []
|
|
for i in self.funcs:
|
|
nlist.append(self.funcs[i])
|
|
return nlist
|
|
|
|
class library_c:
|
|
def __init__(self, name, path):
|
|
self.name = name
|
|
self.path = path
|
|
self.objs = {}
|
|
|
|
def append(self, obj, path, func):
|
|
if obj not in self.objs:
|
|
self.objs[obj] = object_c(obj, path, self.name)
|
|
self.objs[obj].append(func)
|
|
|
|
class libraries_c:
|
|
def __init__(self):
|
|
self.libs = {}
|
|
|
|
def append(self, lib, lib_path, obj, obj_path, func):
|
|
if lib not in self.libs:
|
|
self.libs[lib] = library_c(lib, lib_path)
|
|
self.libs[lib].append(obj, obj_path, func)
|
|
|
|
def dump(self):
|
|
for libname in self.libs:
|
|
lib = self.libs[libname]
|
|
for objname in lib.objs:
|
|
obj = lib.objs[objname]
|
|
print(f'{libname}, {objname}, {obj.path}, {obj.funcs}')
|
|
|
|
class paths_c:
|
|
def __init__(self):
|
|
self.paths = {}
|
|
|
|
def append(self, lib, obj, path):
|
|
if '$IDF_PATH' in path:
|
|
path = path.replace('$IDF_PATH', os.environ['IDF_PATH'])
|
|
|
|
if lib not in self.paths:
|
|
self.paths[lib] = {}
|
|
if obj not in self.paths[lib]:
|
|
self.paths[lib][obj] = []
|
|
self.paths[lib][obj].append(path)
|
|
|
|
def index(self, lib, obj):
|
|
if lib not in self.paths:
|
|
return None
|
|
if '*' in self.paths[lib]:
|
|
obj = '*'
|
|
return self.paths[lib].get(obj)
|
|
|
|
def generator(library_file, object_file, function_file, sdkconfig_file, missing_function_info, objdump='riscv32-esp-elf-objdump'):
|
|
global espidf_objdump, espidf_missing_function_info
|
|
espidf_objdump = objdump
|
|
espidf_missing_function_info = missing_function_info
|
|
|
|
sdkconfig = sdkconfig_c(sdkconfig_file)
|
|
|
|
lib_paths = paths_c()
|
|
with open(library_file, newline='') as csvfile:
|
|
reader = csv.DictReader(csvfile)
|
|
for row in reader:
|
|
lib_paths.append(row['library'], '*', row['path'])
|
|
|
|
obj_paths = paths_c()
|
|
with open(object_file, newline='') as csvfile:
|
|
reader = csv.DictReader(csvfile)
|
|
for row in reader:
|
|
obj_paths.append(row['library'], row['object'], row['path'])
|
|
|
|
libraries = libraries_c()
|
|
with open(function_file, newline='') as csvfile:
|
|
reader = csv.DictReader(csvfile)
|
|
for row in reader:
|
|
if row['option'] and not sdkconfig.check(row['option']):
|
|
print(f'skip {row["function"]}({row["option"]})')
|
|
continue
|
|
lib_path = lib_paths.index(row['library'], '*')
|
|
obj_path = obj_paths.index(row['library'], row['object'])
|
|
if not obj_path:
|
|
obj_path = lib_path
|
|
libraries.append(row['library'], lib_path[0], row['object'], obj_path, row['function'])
|
|
return libraries
|
|
|
|
def main():
|
|
argparser = argparse.ArgumentParser(description='Libraries management')
|
|
|
|
argparser.add_argument(
|
|
'--library', '-l',
|
|
help='Library description file',
|
|
type=str,
|
|
required=True)
|
|
|
|
argparser.add_argument(
|
|
'--object', '-b',
|
|
help='Object description file',
|
|
type=str,
|
|
required=True)
|
|
|
|
argparser.add_argument(
|
|
'--function', '-f',
|
|
help='Function description file',
|
|
type=str,
|
|
required=True)
|
|
|
|
argparser.add_argument(
|
|
'--sdkconfig', '-s',
|
|
help='sdkconfig file',
|
|
type=str,
|
|
required=True)
|
|
|
|
args = argparser.parse_args()
|
|
|
|
libraries = generator(args.library, args.object, args.function, args.sdkconfig, espidf_missing_function_info)
|
|
# libraries.dump()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|