diff options
Diffstat (limited to 'src/mlia/backend/install.py')
-rw-r--r-- | src/mlia/backend/install.py | 250 |
1 files changed, 67 insertions, 183 deletions
diff --git a/src/mlia/backend/install.py b/src/mlia/backend/install.py index 37a277b..c76e3e2 100644 --- a/src/mlia/backend/install.py +++ b/src/mlia/backend/install.py @@ -11,17 +11,12 @@ from abc import abstractmethod from dataclasses import dataclass from pathlib import Path from typing import Callable -from typing import Iterable from typing import Optional from typing import Union -from mlia.backend.executor.runner import BackendRunner -from mlia.backend.executor.system import remove_system +from mlia.backend.repo import get_backend_repository from mlia.utils.download import DownloadArtifact from mlia.utils.filesystem import all_files_exist -from mlia.utils.filesystem import all_paths_valid -from mlia.utils.filesystem import copy_all -from mlia.utils.filesystem import get_mlia_resources from mlia.utils.filesystem import temp_directory from mlia.utils.filesystem import working_directory from mlia.utils.py_manager import get_package_manager @@ -29,52 +24,6 @@ from mlia.utils.py_manager import get_package_manager logger = logging.getLogger(__name__) -# Mapping backend -> device_type -> system_name -_SUPPORTED_SYSTEMS = { - "Corstone-300": { - "Ethos-U55": "Corstone-300: Cortex-M55+Ethos-U55", - "Ethos-U65": "Corstone-300: Cortex-M55+Ethos-U65", - "ethos-u55": "Corstone-300: Cortex-M55+Ethos-U55", - "ethos-u65": "Corstone-300: Cortex-M55+Ethos-U65", - }, - "Corstone-310": { - "Ethos-U55": "Corstone-310: Cortex-M85+Ethos-U55", - "Ethos-U65": "Corstone-310: Cortex-M85+Ethos-U65", - "ethos-u55": "Corstone-310: Cortex-M85+Ethos-U55", - "ethos-u65": "Corstone-310: Cortex-M85+Ethos-U65", - }, -} - -# Mapping system_name -> application -_SYSTEM_TO_APP_MAP = { - "Corstone-300: Cortex-M55+Ethos-U55": "Generic Inference Runner: Ethos-U55", - "Corstone-300: Cortex-M55+Ethos-U65": "Generic Inference Runner: Ethos-U65", - "Corstone-310: Cortex-M85+Ethos-U55": "Generic Inference Runner: Ethos-U55", - "Corstone-310: Cortex-M85+Ethos-U65": "Generic Inference Runner: Ethos-U65", -} - - -def get_system_name(backend: str, device_type: str) -> str: - """Get the system name for the given backend and device type.""" - return _SUPPORTED_SYSTEMS[backend][device_type] - - -def get_application_name(system_name: str) -> str: - """Get application name for the provided system name.""" - return _SYSTEM_TO_APP_MAP[system_name] - - -def get_all_system_names(backend: str) -> list[str]: - """Get all systems supported by the backend.""" - return list(_SUPPORTED_SYSTEMS.get(backend, {}).values()) - - -def get_all_application_names(backend: str) -> list[str]: - """Get all applications supported by the backend.""" - app_set = {_SYSTEM_TO_APP_MAP[sys] for sys in get_all_system_names(backend)} - return list(app_set) - - @dataclass class InstallFromPath: """Installation from the local path.""" @@ -95,29 +44,24 @@ InstallationType = Union[InstallFromPath, DownloadAndInstall] class Installation(ABC): """Base class for the installation process of the backends.""" - @property - @abstractmethod - def name(self) -> str: - """Return name of the backend.""" - - @property - @abstractmethod - def description(self) -> str: - """Return description of the backend.""" + def __init__(self, name: str, description: str) -> None: + """Init the installation.""" + self.name = name + self.description = description @property @abstractmethod def could_be_installed(self) -> bool: - """Return true if backend could be installed in current environment.""" + """Check if backend could be installed in current environment.""" @property @abstractmethod def already_installed(self) -> bool: - """Return true if backend is already installed.""" + """Check if backend is already installed.""" @abstractmethod def supports(self, install_type: InstallationType) -> bool: - """Return true if installation supports requested installation type.""" + """Check if installation supports requested installation type.""" @abstractmethod def install(self, install_type: InstallationType) -> None: @@ -134,103 +78,53 @@ class BackendInfo: backend_path: Path copy_source: bool = True - system_config: str | None = None + settings: dict | None = None PathChecker = Callable[[Path], Optional[BackendInfo]] BackendInstaller = Callable[[bool, Path], Path] -class BackendMetadata: - """Backend installation metadata.""" +class BackendInstallation(Installation): + """Backend installation.""" def __init__( self, name: str, description: str, - system_config: str, - apps_resources: list[str], fvp_dir_name: str, download_artifact: DownloadArtifact | None, - supported_platforms: list[str] | None = None, + supported_platforms: list[str] | None, + path_checker: PathChecker, + backend_installer: BackendInstaller | None, ) -> None: - """ - Initialize BackendMetadata. + """Init the backend installation.""" + super().__init__(name, description) - Members expected_systems and expected_apps are filled automatically. - """ - self.name = name - self.description = description - self.system_config = system_config - self.apps_resources = apps_resources self.fvp_dir_name = fvp_dir_name self.download_artifact = download_artifact self.supported_platforms = supported_platforms - - self.expected_systems = get_all_system_names(name) - self.expected_apps = get_all_application_names(name) - - @property - def expected_resources(self) -> Iterable[Path]: - """Return list of expected resources.""" - resources = [self.system_config, *self.apps_resources] - - return (get_mlia_resources() / resource for resource in resources) - - @property - def supported_platform(self) -> bool: - """Return true if current platform supported.""" - if not self.supported_platforms: - return True - - return platform.system() in self.supported_platforms - - -class BackendInstallation(Installation): - """Backend installation.""" - - def __init__( - self, - backend_runner: BackendRunner, - metadata: BackendMetadata, - path_checker: PathChecker, - backend_installer: BackendInstaller | None, - ) -> None: - """Init the backend installation.""" - self.backend_runner = backend_runner - self.metadata = metadata self.path_checker = path_checker self.backend_installer = backend_installer @property - def name(self) -> str: - """Return name of the backend.""" - return self.metadata.name - - @property - def description(self) -> str: - """Return description of the backend.""" - return self.metadata.description - - @property def already_installed(self) -> bool: """Return true if backend already installed.""" - return self.backend_runner.all_installed( - self.metadata.expected_systems, self.metadata.expected_apps - ) + backend_repo = get_backend_repository() + return backend_repo.is_backend_installed(self.name) @property def could_be_installed(self) -> bool: """Return true if backend could be installed.""" - if not self.metadata.supported_platform: - return False - - return all_paths_valid(self.metadata.expected_resources) + return ( + not self.supported_platforms + or platform.system() in self.supported_platforms + ) def supports(self, install_type: InstallationType) -> bool: """Return true if backends supported type of the installation.""" if isinstance(install_type, DownloadAndInstall): - return self.metadata.download_artifact is not None + return self.download_artifact is not None if isinstance(install_type, InstallFromPath): return self.path_checker(install_type.backend_path) is not None @@ -240,41 +134,38 @@ class BackendInstallation(Installation): def install(self, install_type: InstallationType) -> None: """Install the backend.""" if isinstance(install_type, DownloadAndInstall): - download_artifact = self.metadata.download_artifact - assert download_artifact is not None, "No artifact provided" + assert self.download_artifact is not None, "No artifact provided" - self.download_and_install(download_artifact, install_type.eula_agreement) + self._download_and_install( + self.download_artifact, install_type.eula_agreement + ) elif isinstance(install_type, InstallFromPath): - backend_path = self.path_checker(install_type.backend_path) - assert backend_path is not None, "Unable to resolve backend path" + backend_info = self.path_checker(install_type.backend_path) - self.install_from(backend_path) + assert backend_info is not None, "Unable to resolve backend path" + self._install_from(backend_info) else: raise Exception(f"Unable to install {install_type}") - def install_from(self, backend_info: BackendInfo) -> None: + def _install_from(self, backend_info: BackendInfo) -> None: """Install backend from the directory.""" - mlia_resources = get_mlia_resources() - - with temp_directory() as tmpdir: - fvp_dist_dir = tmpdir / self.metadata.fvp_dir_name - - system_config = self.metadata.system_config - if backend_info.system_config: - system_config = backend_info.system_config - - resources_to_copy = [mlia_resources / system_config] - if backend_info.copy_source: - resources_to_copy.append(backend_info.backend_path) - - copy_all(*resources_to_copy, dest=fvp_dist_dir) - - self.backend_runner.install_system(fvp_dist_dir) - - for app in self.metadata.apps_resources: - self.backend_runner.install_application(mlia_resources / app) + backend_repo = get_backend_repository() + + if backend_info.copy_source: + backend_repo.copy_backend( + self.name, + backend_info.backend_path, + self.fvp_dir_name, + backend_info.settings, + ) + else: + backend_repo.add_backend( + self.name, + backend_info.backend_path, + backend_info.settings, + ) - def download_and_install( + def _download_and_install( self, download_artifact: DownloadArtifact, eula_agrement: bool ) -> None: """Download and install the backend.""" @@ -288,11 +179,10 @@ class BackendInstallation(Installation): with tarfile.open(downloaded_to) as archive: archive.extractall(dist_dir) - assert self.backend_installer, ( - f"Backend '{self.metadata.name}' does not support " - "download and installation." - ) - backend_path = self.backend_installer(eula_agrement, dist_dir) + backend_path = dist_dir + if self.backend_installer: + backend_path = self.backend_installer(eula_agrement, dist_dir) + if self.path_checker(backend_path) is None: raise Exception("Downloaded artifact has invalid structure") @@ -300,18 +190,23 @@ class BackendInstallation(Installation): def uninstall(self) -> None: """Uninstall the backend.""" - remove_system(self.metadata.fvp_dir_name) + backend_repo = get_backend_repository() + backend_repo.remove_backend(self.name) class PackagePathChecker: """Package path checker.""" def __init__( - self, expected_files: list[str], backend_subfolder: str | None = None + self, + expected_files: list[str], + backend_subfolder: str | None = None, + settings: dict = None, ) -> None: """Init the path checker.""" self.expected_files = expected_files self.backend_subfolder = backend_subfolder + self.settings = settings def __call__(self, backend_path: Path) -> BackendInfo | None: """Check if directory contains all expected files.""" @@ -319,15 +214,14 @@ class PackagePathChecker: if not all_files_exist(resolved_paths): return None + actual_backend_path = backend_path if self.backend_subfolder: subfolder = backend_path / self.backend_subfolder - if not subfolder.is_dir(): - return None + if subfolder.is_dir(): + actual_backend_path = subfolder - return BackendInfo(subfolder) - - return BackendInfo(backend_path) + return BackendInfo(actual_backend_path, settings=self.settings) class StaticPathChecker: @@ -338,13 +232,13 @@ class StaticPathChecker: static_backend_path: Path, expected_files: list[str], copy_source: bool = False, - system_config: str | None = None, + settings: dict | None = None, ) -> None: """Init static path checker.""" self.static_backend_path = static_backend_path self.expected_files = expected_files self.copy_source = copy_source - self.system_config = system_config + self.settings = settings def __call__(self, backend_path: Path) -> BackendInfo | None: """Check if directory equals static backend path with all expected files.""" @@ -358,7 +252,7 @@ class StaticPathChecker: return BackendInfo( backend_path, copy_source=self.copy_source, - system_config=self.system_config, + settings=self.settings, ) @@ -392,8 +286,8 @@ class PyPackageBackendInstallation(Installation): expected_packages: list[str], ) -> None: """Init the backend installation.""" - self._name = name - self._description = description + super().__init__(name, description) + self._packages_to_install = packages_to_install self._packages_to_uninstall = packages_to_uninstall self._expected_packages = expected_packages @@ -401,16 +295,6 @@ class PyPackageBackendInstallation(Installation): self.package_manager = get_package_manager() @property - def name(self) -> str: - """Return name of the backend.""" - return self._name - - @property - def description(self) -> str: - """Return description of the backend.""" - return self._description - - @property def could_be_installed(self) -> bool: """Check if backend could be installed.""" return True |