diff --git a/scripts/setup/nrfconnect/zap_common.py b/scripts/setup/nrfconnect/zap_common.py index d32e28e30b..01d50b35b3 100644 --- a/scripts/setup/nrfconnect/zap_common.py +++ b/scripts/setup/nrfconnect/zap_common.py @@ -67,44 +67,74 @@ def unzip(zip: Path, out: Path): else: raise RuntimeError(f"Unsupported platform: {current_os}") - def get_zap_path(self): + def get_install_path(self) -> Path: + """ + Returns ZAP package installation directory. + """ + return self.install_path + + def get_zap_path(self) -> Path: + """ + Returns path to ZAP GUI. + """ return self.install_path / self.zap_exe - def get_zap_cli_path(self): + def get_zap_cli_path(self) -> Path: + """ + Returns path to ZAP CLI. + """ return self.install_path / self.zap_cli_exe - def get_recommended_version(self): + def get_recommended_version(self) -> Tuple[int, int, int]: + """ + Returns ZAP package recommended version as a tuple of integers. + + Parses zap_execution.py script from Matter SDK to determine the minimum + required ZAP package version. + """ + RE_MIN_ZAP_VERSION = r'MIN_ZAP_VERSION\s*=\s*\'(\d+)\.(\d+)\.(\d+)' zap_execution_path = self.matter_path / 'scripts/tools/zap/zap_execution.py' with open(zap_execution_path, 'r') as f: - if match := re.search(r'MIN_ZAP_VERSION\s*=\s*\'(\d+)\.(\d+)\.(\d+)', f.read()): + if match := re.search(RE_MIN_ZAP_VERSION, f.read()): return tuple(int(group) for group in match.groups()) - raise RuntimeError("Failed to find MIN_ZAP_VERSION pattern in {zap_execution_path}") + raise RuntimeError("Failed to find MIN_ZAP_VERSION in {zap_execution_path}") + + def get_current_version(self) -> Tuple[int, int, int]: + """ + Returns ZAP package current version as a tuple of integers. - def get_current_version(self): + Parses the output of `zap --version` to determine the current ZAP + package version. If the ZAP package has not been installed yet, + the method returns None. + """ try: output = subprocess.check_output([self.get_zap_path(), '--version']).decode('ascii').strip() except Exception as e: return None - if match := re.search(r'Version:\s*(\d+)\.(\d+)\.(\d+)', output): + RE_VERSION = r'Version:\s*(\d+)\.(\d+)\.(\d+)' + if match := re.search(RE_VERSION, output): return tuple(int(group) for group in match.groups()) - raise RuntimeError("Failed to find \"Version:\" pattern in zap output") + raise RuntimeError("Failed to find version in ZAP output") - def install_zap(self, version: Tuple[int, int, int]): + def install_zap(self, version: Tuple[int, int, int]) -> None: + """ + Downloads and unpacks selected ZAP package version. + """ with tempfile.TemporaryDirectory() as temp_dir: url = ZapInstaller.ZAP_URL_PATTERN % (*version, self.package) log.inf(f'Downloading {url}...') try: wget.download(url, out=temp_dir) - log.inf('') except Exception as e: - raise RuntimeError(f'Failed do download ZAP package from {url}: {e}') + raise RuntimeError(f'Failed to download ZAP package from {url}: {e}') shutil.rmtree(self.install_path, ignore_errors=True) + log.inf('') # Fix console after displaying wget progress bar log.inf(f'Unzipping ZAP package to {self.install_path}...') try: @@ -112,10 +142,16 @@ def install_zap(self, version: Tuple[int, int, int]): except Exception as e: raise RuntimeError('Failed to unzip ZAP package: {e}') - ZapInstaller.set_x(self.get_zap_path()) - ZapInstaller.set_x(self.get_zap_cli_path()) + ZapInstaller.set_exec_permission(self.get_zap_path()) + ZapInstaller.set_exec_permission(self.get_zap_cli_path()) def update_zap_if_needed(self): + """ + Installs ZAP package if not up to date. + + Installs or overrides the previous ZAP package installation if the + current version does not match the recommended version. + """ recommended_version = self.get_recommended_version() current_version = self.get_current_version() @@ -130,5 +166,5 @@ def update_zap_if_needed(self): self.install_zap(recommended_version) @staticmethod - def set_x(path: Path): - os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) \ No newline at end of file + def set_exec_permission(path: Path): + os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) diff --git a/scripts/setup/nrfconnect/zap_gui.py b/scripts/setup/nrfconnect/zap_gui.py index c06a21a913..c6d22e20b1 100644 --- a/scripts/setup/nrfconnect/zap_gui.py +++ b/scripts/setup/nrfconnect/zap_gui.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause import argparse +import os import sys from pathlib import Path @@ -13,6 +14,8 @@ from west import log from west.commands import WestCommand +MATTER_PATH = Path(__file__).parents[3] + class ZapGui(WestCommand): def __init__(self): @@ -31,25 +34,25 @@ def do_add_parser(self, parser_adder): self.name, help=self.help, formatter_class=argparse.RawDescriptionHelpFormatter, description=self.description) - parser.add_argument('-z', '--zap-file', type=Path, default=find_zap(), + parser.add_argument('-z', '--zap-file', type=Path, help='Path to data model configuration file (*.zap)') parser.add_argument('-j', '--zcl-json', type=Path, help='Path to data model definition file (zcl.json)') return parser def do_run(self, args, unknown_args): - MATTER_PATH = str(Path(self.manifest.path).parent.absolute() / "../modules/lib/matter") - if args.zcl_json is None: - args.zcl_json = MATTER_PATH + "/src/app/zap-templates/zcl/zcl.json" - - APP_TEMPLATES = MATTER_PATH + "/src/app/zap-templates/app-templates.json" + zap_file_path = args.zap_file or find_zap() + zcl_json_path = args.zcl_json or MATTER_PATH / 'src/app/zap-templates/zcl/zcl.json' + app_templates_path = MATTER_PATH / 'src/app/zap-templates/app-templates.json' zap_installer = ZapInstaller(Path(MATTER_PATH)) zap_installer.update_zap_if_needed() + zap_cache_path = zap_installer.get_install_path() / ".zap" - cmd = [zap_installer.get_zap_path().name] - cmd += [args.zap_file.name] if args.zap_file.name else [] - cmd += ["--zcl", args.zcl_json] - cmd += ["--gen", APP_TEMPLATES] + cmd = [str(zap_installer.get_zap_path())] + cmd += [str(zap_file_path)] if zap_file_path else [] + cmd += ["--zcl", str(zcl_json_path)] + cmd += ["--gen", str(app_templates_path)] + cmd += ["--stateDirectory", str(zap_cache_path)] self.check_call(cmd)