From a6ec6f7bd1a29ac2b1caa43fbff5c0e976d17fe1 Mon Sep 17 00:00:00 2001 From: Damian Krolik Date: Fri, 17 May 2024 17:07:05 +0200 Subject: [PATCH] [nrf noup] ZAP west command improvements 1. If multiple ZAP files are detected in the given location, ask a user to choose the one to edit. 2. Allow to provide a Matter SDK path to enable using the commands with the upstream repo. Signed-off-by: Damian Krolik --- scripts/west/zap_common.py | 66 ++++++++++++++++++++++++++---------- scripts/west/zap_generate.py | 10 +++--- scripts/west/zap_gui.py | 11 +++--- 3 files changed, 61 insertions(+), 26 deletions(-) diff --git a/scripts/west/zap_common.py b/scripts/west/zap_common.py index 1af23cae59..ce7583d9aa 100644 --- a/scripts/west/zap_common.py +++ b/scripts/west/zap_common.py @@ -12,29 +12,49 @@ import tempfile import wget +from collections import deque from pathlib import Path from typing import Tuple from zipfile import ZipFile from west import log -MATTER_PATH = Path(__file__).parents[2] +DEFAULT_MATTER_PATH = Path(__file__).parents[2] -def find_zap(root: Path = Path.cwd(), max_depth: int = 1): +def find_zap(root: Path = Path.cwd(), max_depth: int = 2): """ Find *.zap file in the given directory or its subdirectories. """ - subdirs = [] - for name in root.iterdir(): - if name.is_file() and (name.suffix.lower() == '.zap'): - return root / name - if name.is_dir() and (max_depth > 0): - subdirs.append(name) - for subdir in subdirs: - if zap := find_zap(root / subdir, max_depth - 1): - return zap - return None + zap_files = [] + search_dirs = deque() + search_dirs.append((root, max_depth)) + + while search_dirs: + search_dir, max_depth = search_dirs.popleft() + + for name in search_dir.iterdir(): + if name.is_file() and (name.suffix.lower() == '.zap'): + zap_files.append(search_dir / name) + continue + if name.is_dir() and (max_depth > 0): + search_dirs.append((search_dir / name, max_depth - 1)) + + # At most one ZAP file found in the selected location, return immediately. + if len(zap_files) <= 1: + return zap_files[0] if zap_files else None + + # Otherwise, ask a user to choose the ZAP file to edit. + for i, zap_file in enumerate(zap_files): + print(f'{i}. {zap_file.relative_to(root)}') + + while True: + try: + maxind = len(zap_files) - 1 + prompt = f'Select file to edit (0-{maxind}): ' + return zap_files[int(input(prompt))] + except Exception: + pass def existing_file_path(arg: str) -> Path: @@ -47,6 +67,16 @@ def existing_file_path(arg: str) -> Path: raise argparse.ArgumentTypeError(f'invalid file path: \'{arg}\'') +def existing_dir_path(arg: str) -> Path: + """ + Helper function to validate directory path argument. + """ + p = Path(arg) + if p.is_dir(): + return p + raise argparse.ArgumentTypeError(f'invalid directory path: \'{arg}\'') + + class ZapInstaller: INSTALL_DIR = Path('.zap-install') ZAP_URL_PATTERN = 'https://github.com/project-chip/zap/releases/download/v%04d.%02d.%02d-nightly/%s.zip' @@ -171,15 +201,15 @@ def update_zap_if_needed(self) -> None: recommended_version = self.get_recommended_version() current_version = self.get_current_version() - if current_version == recommended_version: - log.inf('ZAP is up to date: {0}.{1}.{2}'.format(*recommended_version)) - return + log.inf(f'ZAP installation directory: {self.install_path}') if current_version: - log.inf('Found ZAP version: {0}.{1}.{2}'.format(*current_version)) + verdict = 'up to date' if current_version == recommended_version else 'outdated' + log.inf('Found ZAP {}.{}.{} ({})'.format(*current_version, verdict)) - log.inf('Installing ZAP version: {0}.{1}.{2}'.format(*recommended_version)) - self.install_zap(recommended_version) + if current_version != recommended_version: + log.inf('Installing ZAP {}.{}.{}'.format(*recommended_version)) + self.install_zap(recommended_version) @staticmethod def set_exec_permission(path: Path) -> None: diff --git a/scripts/west/zap_generate.py b/scripts/west/zap_generate.py index a20d5d90b3..05707e3124 100644 --- a/scripts/west/zap_generate.py +++ b/scripts/west/zap_generate.py @@ -12,7 +12,7 @@ from west import log from west.commands import CommandError, WestCommand -from zap_common import existing_file_path, find_zap, ZapInstaller, MATTER_PATH +from zap_common import existing_file_path, existing_dir_path, find_zap, ZapInstaller, DEFAULT_MATTER_PATH class ZapGenerate(WestCommand): @@ -35,6 +35,8 @@ def do_add_parser(self, parser_adder): help='Path to data model configuration file (*.zap)') parser.add_argument('-o', '--output', type=Path, help='Path where to store the generated files') + parser.add_argument('-m', '--matter-path', type=existing_dir_path, + default=DEFAULT_MATTER_PATH, help='Path to Matter SDK') return parser def do_run(self, args, unknown_args): @@ -51,10 +53,10 @@ def do_run(self, args, unknown_args): else: output_path = zap_file_path.parent / "zap-generated" - app_templates_path = MATTER_PATH / "src/app/zap-templates/app-templates.json" - zap_generate_path = MATTER_PATH / "scripts/tools/zap/generate.py" + app_templates_path = args.matter_path / "src/app/zap-templates/app-templates.json" + zap_generate_path = args.matter_path / "scripts/tools/zap/generate.py" - zap_installer = ZapInstaller(MATTER_PATH) + zap_installer = ZapInstaller(args.matter_path) zap_installer.update_zap_if_needed() # make sure that the generate.py script uses the proper zap_cli binary (handled by west) diff --git a/scripts/west/zap_gui.py b/scripts/west/zap_gui.py index d321e0af39..e9081b55fb 100644 --- a/scripts/west/zap_gui.py +++ b/scripts/west/zap_gui.py @@ -7,9 +7,10 @@ from pathlib import Path from textwrap import dedent -from zap_common import existing_file_path, find_zap, ZapInstaller, MATTER_PATH from west.commands import WestCommand +from zap_common import existing_file_path, existing_dir_path, find_zap, ZapInstaller, DEFAULT_MATTER_PATH + class ZapGui(WestCommand): @@ -33,6 +34,8 @@ def do_add_parser(self, parser_adder): help='Path to data model configuration file (*.zap)') parser.add_argument('-j', '--zcl-json', type=existing_file_path, help='Path to data model definition file (zcl.json)') + parser.add_argument('-m', '--matter-path', type=existing_dir_path, + default=DEFAULT_MATTER_PATH, help='Path to Matter SDK') return parser def do_run(self, args, unknown_args): @@ -44,11 +47,11 @@ def do_run(self, args, unknown_args): if args.zcl_json: zcl_json_path = args.zcl_json.absolute() else: - zcl_json_path = MATTER_PATH / 'src/app/zap-templates/zcl/zcl.json' + zcl_json_path = args.matter_path / 'src/app/zap-templates/zcl/zcl.json' - app_templates_path = MATTER_PATH / 'src/app/zap-templates/app-templates.json' + app_templates_path = args.matter_path / 'src/app/zap-templates/app-templates.json' - zap_installer = ZapInstaller(Path(MATTER_PATH)) + zap_installer = ZapInstaller(args.matter_path) zap_installer.update_zap_if_needed() zap_cache_path = zap_installer.get_install_path() / ".zap"