| # |
| # Copyright (C) 2019 Bloomberg Finance LP |
| # |
| # This program is free software; you can redistribute it and/or |
| # modify it under the terms of the GNU Lesser General Public |
| # License as published by the Free Software Foundation; either |
| # version 2 of the License, or (at your option) any later version. |
| # |
| # This library is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| # Lesser General Public License for more details. |
| # |
| # You should have received a copy of the GNU Lesser General Public |
| # License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| # |
| # Authors: |
| # James Ennis <james.ennis@codethink.co.uk> |
| |
| from typing import TYPE_CHECKING |
| |
| from . import Element |
| from . import _cachekey |
| from ._exceptions import ArtifactElementError |
| from ._loader.metaelement import MetaElement |
| from .types import Scope |
| |
| if TYPE_CHECKING: |
| from typing import Dict |
| |
| |
| # ArtifactElement() |
| # |
| # Object to be used for directly processing an artifact |
| # |
| # Args: |
| # context (Context): The Context object |
| # ref (str): The artifact ref |
| # |
| class ArtifactElement(Element): |
| |
| # A hash of ArtifactElement by ref |
| __instantiated_artifacts = {} # type: Dict[str, ArtifactElement] |
| |
| # ArtifactElement's require this as the sandbox will use a normal |
| # directory when we checkout |
| BST_VIRTUAL_DIRECTORY = True |
| |
| def __init__(self, context, ref): |
| _, element, key = verify_artifact_ref(ref) |
| |
| self._ref = ref |
| self._key = key |
| |
| project = context.get_toplevel_project() |
| meta = MetaElement(project, element) # NOTE element has no .bst suffix |
| plugin_conf = None |
| |
| super().__init__(context, project, meta, plugin_conf) |
| |
| # _new_from_artifact_ref(): |
| # |
| # Recursively instantiate a new ArtifactElement instance, and its |
| # dependencies from an artifact ref |
| # |
| # Args: |
| # ref (String): The artifact ref |
| # context (Context): The Context object |
| # task (Task): A task object to report progress to |
| # |
| # Returns: |
| # (ArtifactElement): A newly created Element instance |
| # |
| @classmethod |
| def _new_from_artifact_ref(cls, ref, context, task=None): |
| |
| if ref in cls.__instantiated_artifacts: |
| return cls.__instantiated_artifacts[ref] |
| |
| artifact_element = ArtifactElement(context, ref) |
| # XXX: We need to call initialize_state as it is responsible for |
| # initialising an Element/ArtifactElement's Artifact (__artifact) |
| artifact_element._initialize_state() |
| cls.__instantiated_artifacts[ref] = artifact_element |
| |
| for dep_ref in artifact_element.get_dependency_refs(Scope.BUILD): |
| dependency = ArtifactElement._new_from_artifact_ref(dep_ref, context, task) |
| artifact_element._add_build_dependency(dependency) |
| |
| return artifact_element |
| |
| # _clear_artifact_refs_cache() |
| # |
| # Clear the internal artifact refs cache |
| # |
| # When loading ArtifactElements from artifact refs, we cache already |
| # instantiated ArtifactElements in order to not have to load the same |
| # ArtifactElements twice. This clears the cache. |
| # |
| # It should be called whenever we are done loading all artifacts in order |
| # to save memory. |
| # |
| @classmethod |
| def _clear_artifact_refs_cache(cls): |
| cls.__instantiated_artifacts = {} |
| |
| # Override Element.get_artifact_name() |
| def get_artifact_name(self, key=None): |
| return self._ref |
| |
| # Dummy configure method |
| def configure(self, node): |
| pass |
| |
| # Dummy preflight method |
| def preflight(self): |
| pass |
| |
| # get_dependency_refs() |
| # |
| # Obtain the refs of a particular scope of dependencies |
| # |
| # Args: |
| # scope (Scope): The scope of dependencies for which we want to obtain the refs |
| # |
| # Returns: |
| # (list [str]): A list of artifact refs |
| # |
| def get_dependency_refs(self, scope=Scope.BUILD): |
| artifact = self._get_artifact() |
| return artifact.get_dependency_refs(deps=scope) |
| |
| # configure_sandbox() |
| # |
| # Configure a sandbox for installing artifacts into |
| # |
| # Args: |
| # sandbox (Sandbox) |
| # |
| def configure_sandbox(self, sandbox): |
| install_root = self.get_variable("install-root") |
| |
| # Tell the sandbox to mount the build root and install root |
| sandbox.mark_directory(install_root) |
| |
| # Tell sandbox which directory is preserved in the finished artifact |
| sandbox.set_output_directory(install_root) |
| |
| # Override Element._calculate_cache_key |
| def _calculate_cache_key(self, dependencies=None): |
| return self._key |
| |
| # Override Element._get_cache_key() |
| def _get_cache_key(self, strength=None): |
| return self._key |
| |
| |
| # verify_artifact_ref() |
| # |
| # Verify that a ref string matches the format of an artifact |
| # |
| # Args: |
| # ref (str): The artifact ref |
| # |
| # Returns: |
| # project (str): The project's name |
| # element (str): The element's name |
| # key (str): The cache key |
| # |
| # Raises: |
| # ArtifactElementError if the ref string does not match |
| # the expected format |
| # |
| def verify_artifact_ref(ref): |
| try: |
| project, element, key = ref.split("/", 2) # This will raise a Value error if unable to split |
| # Explicitly raise a ValueError if the key length is not as expected |
| if not _cachekey.is_key(key): |
| raise ValueError |
| except ValueError: |
| raise ArtifactElementError("Artifact: {} is not of the expected format".format(ref)) |
| |
| return project, element, key |