aboutsummaryrefslogtreecommitdiff
path: root/src/mlia/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/mlia/backend')
-rw-r--r--src/mlia/backend/corstone/install.py9
-rw-r--r--src/mlia/backend/corstone/performance.py8
-rw-r--r--src/mlia/backend/install.py40
-rw-r--r--src/mlia/backend/manager.py56
4 files changed, 84 insertions, 29 deletions
diff --git a/src/mlia/backend/corstone/install.py b/src/mlia/backend/corstone/install.py
index 5f11d5b..5c18334 100644
--- a/src/mlia/backend/corstone/install.py
+++ b/src/mlia/backend/corstone/install.py
@@ -19,7 +19,7 @@ from mlia.backend.install import CompoundPathChecker
from mlia.backend.install import Installation
from mlia.backend.install import PackagePathChecker
from mlia.backend.install import StaticPathChecker
-from mlia.utils.download import DownloadArtifact
+from mlia.utils.download import DownloadConfig
from mlia.utils.filesystem import working_directory
@@ -159,8 +159,6 @@ def get_corstone_installation(corstone_name: str) -> Installation:
archive = corstone_fvp.archive
sha256_hash = corstone_fvp.sha256_hash
url = ARM_ECOSYSTEM_FVP_URL + archive
- filename = archive.split("/")[-1]
- version = corstone_fvp.get_fvp_version()
expected_files_fvp = corstone_fvp.fvp_expected_files
expected_files_vht = corstone_fvp.get_vht_expected_files()
backend_subfolder = expected_files_fvp[0].split("FVP")[0]
@@ -169,11 +167,8 @@ def get_corstone_installation(corstone_name: str) -> Installation:
name=corstone_name,
description=corstone_name.capitalize() + " FVP",
fvp_dir_name=corstone_name.replace("-", "_"),
- download_artifact=DownloadArtifact(
- name=corstone_name.capitalize() + " FVP",
+ download_config=DownloadConfig(
url=url,
- filename=filename,
- version=version,
sha256_hash=sha256_hash,
),
supported_platforms=["Linux"],
diff --git a/src/mlia/backend/corstone/performance.py b/src/mlia/backend/corstone/performance.py
index fc50109..fe4e271 100644
--- a/src/mlia/backend/corstone/performance.py
+++ b/src/mlia/backend/corstone/performance.py
@@ -15,6 +15,7 @@ from mlia.backend.errors import BackendExecutionFailed
from mlia.backend.repo import get_backend_repository
from mlia.utils.filesystem import get_mlia_resources
from mlia.utils.proc import Command
+from mlia.utils.proc import OutputLogger
from mlia.utils.proc import process_command_output
@@ -187,15 +188,12 @@ def get_metrics(
) from err
output_parser = GenericInferenceOutputParser()
-
- def redirect_to_log(line: str) -> None:
- """Redirect FVP output to the logger."""
- logger.debug(line.strip())
+ output_logger = OutputLogger(logger)
try:
process_command_output(
command,
- [output_parser, redirect_to_log],
+ [output_parser, output_logger],
)
except subprocess.CalledProcessError as err:
raise BackendExecutionFailed("Backend execution failed.") from err
diff --git a/src/mlia/backend/install.py b/src/mlia/backend/install.py
index 0ced9f6..1a7d58b 100644
--- a/src/mlia/backend/install.py
+++ b/src/mlia/backend/install.py
@@ -16,7 +16,8 @@ from typing import Optional
from typing import Union
from mlia.backend.repo import get_backend_repository
-from mlia.utils.download import DownloadArtifact
+from mlia.utils.download import download
+from mlia.utils.download import DownloadConfig
from mlia.utils.filesystem import all_files_exist
from mlia.utils.filesystem import temp_directory
from mlia.utils.filesystem import working_directory
@@ -45,10 +46,18 @@ InstallationType = Union[InstallFromPath, DownloadAndInstall]
class Installation(ABC):
"""Base class for the installation process of the backends."""
- def __init__(self, name: str, description: str) -> None:
+ def __init__(
+ self, name: str, description: str, dependencies: list[str] | None = None
+ ) -> None:
"""Init the installation."""
+ assert not dependencies or name not in dependencies, (
+ f"Invalid backend configuration: Backend '{name}' has itself as a "
+ "dependency! The backend source code needs to be fixed."
+ )
+
self.name = name
self.description = description
+ self.dependencies = dependencies if dependencies else []
@property
@abstractmethod
@@ -89,21 +98,22 @@ BackendInstaller = Callable[[bool, Path], Path]
class BackendInstallation(Installation):
"""Backend installation."""
- def __init__(
+ def __init__( # pylint: disable=too-many-arguments
self,
name: str,
description: str,
fvp_dir_name: str,
- download_artifact: DownloadArtifact | None,
+ download_config: DownloadConfig | None,
supported_platforms: list[str] | None,
path_checker: PathChecker,
backend_installer: BackendInstaller | None,
+ dependencies: list[str] | None = None,
) -> None:
"""Init the backend installation."""
- super().__init__(name, description)
+ super().__init__(name, description, dependencies)
self.fvp_dir_name = fvp_dir_name
- self.download_artifact = download_artifact
+ self.download_config = download_config
self.supported_platforms = supported_platforms
self.path_checker = path_checker
self.backend_installer = backend_installer
@@ -125,7 +135,7 @@ class BackendInstallation(Installation):
def supports(self, install_type: InstallationType) -> bool:
"""Return true if backends supported type of the installation."""
if isinstance(install_type, DownloadAndInstall):
- return self.download_artifact is not None
+ return self.download_config is not None
if isinstance(install_type, InstallFromPath):
return self.path_checker(install_type.backend_path) is not None
@@ -135,10 +145,10 @@ class BackendInstallation(Installation):
def install(self, install_type: InstallationType) -> None:
"""Install the backend."""
if isinstance(install_type, DownloadAndInstall):
- assert self.download_artifact is not None, "No artifact provided"
+ assert self.download_config is not None, "No artifact provided"
self._download_and_install(
- self.download_artifact, install_type.eula_agreement
+ self.download_config, install_type.eula_agreement
)
elif isinstance(install_type, InstallFromPath):
backend_info = self.path_checker(install_type.backend_path)
@@ -207,13 +217,17 @@ class BackendInstallation(Installation):
ex,
)
- def _download_and_install(
- self, download_artifact: DownloadArtifact, eula_agrement: bool
- ) -> None:
+ def _download_and_install(self, cfg: DownloadConfig, eula_agrement: bool) -> None:
"""Download and install the backend."""
with temp_directory() as tmpdir:
try:
- dest = download_artifact.download_to(tmpdir)
+ dest = tmpdir / cfg.filename
+ download(
+ dest=dest,
+ cfg=cfg,
+ show_progress=True,
+ )
+
except Exception as err:
raise RuntimeError("Unable to download backend artifact.") from err
diff --git a/src/mlia/backend/manager.py b/src/mlia/backend/manager.py
index a4bc8c0..a752791 100644
--- a/src/mlia/backend/manager.py
+++ b/src/mlia/backend/manager.py
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: Copyright 2022-2023, Arm Limited and/or its affiliates.
+# SPDX-FileCopyrightText: Copyright 2022-2024, Arm Limited and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
"""Module for installation process."""
from __future__ import annotations
@@ -188,6 +188,33 @@ class DefaultInstallationManager(InstallationManager, InstallationFiltersMixin):
logger.info("%s installation canceled.", installation.name)
return
+ for dependency in installation.dependencies:
+ deps = self.find_by_name(dependency)
+ if not deps:
+ raise ValueError(
+ f"Backend {installation.name} depends on "
+ f"unknown backend '{dependency}'."
+ )
+ missing_deps = [dep for dep in deps if not dep.already_installed]
+ if missing_deps:
+ proceed = self.noninteractive or yes(
+ "The following dependencies are not installed: "
+ f"{[dep.name for dep in missing_deps]}. "
+ "Continue installation anyway?"
+ )
+ logger.warning(
+ "Installing backend %s with the following dependencies "
+ "missing: %s",
+ installation.name,
+ missing_deps,
+ )
+ if not proceed:
+ logger.info(
+ "%s installation canceled due to missing dependencies.",
+ installation.name,
+ )
+ return
+
if installation.already_installed and force:
logger.info(
"Force installing %s, so delete the existing "
@@ -254,16 +281,37 @@ class DefaultInstallationManager(InstallationManager, InstallationFiltersMixin):
installations = self.already_installed(backend_name)
if not installations:
- raise ConfigurationError(f"Backend '{backend_name}' is not installed")
+ raise ConfigurationError(f"Backend '{backend_name}' is not installed.")
if len(installations) != 1:
raise InternalError(
- f"More than one installed backend with name {backend_name} found"
+ f"More than one installed backend with name {backend_name} found."
)
installation = installations[0]
- installation.uninstall()
+ dependent_backends = [
+ dep.name
+ for dep in self.installations
+ if dep.name in installation.dependencies
+ ]
+ if dependent_backends:
+ msg = (
+ f"The following backends depend on '{installation.name}' which "
+ f"you are about to uninstall: {dependent_backends}",
+ )
+ proceed = self.noninteractive or yes(
+ f"{msg}. Continue uninstalling anyway?"
+ )
+ logger.warning(msg)
+ if not proceed:
+ logger.info(
+ "Uninstalling %s canceled due to dependencies.",
+ installation.name,
+ )
+ return
+
+ installation.uninstall()
logger.info("%s successfully uninstalled.", installation.name)
def backend_installed(self, backend_name: str) -> bool: