blob: 778941768fb0432ceb52a6a180cce77493811cc7 [file] [log] [blame]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Authors:
# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
# Jim MacArthur <jim.macarthur@codethink.co.uk>
# Benjamin Schubert <bschubert15@bloomberg.net>
"""
Foundation types
================
"""
from typing import Any, Dict, List, Union, Optional
import os
from .node import MappingNode, SequenceNode
from ._types import MetaFastEnum
class FastEnum(metaclass=MetaFastEnum):
"""
A reimplementation of a subset of the `Enum` functionality, which is far quicker than `Enum`.
:class:`enum.Enum` attributes accesses can be really slow, and slow down the execution noticeably.
This reimplementation doesn't suffer the same problems, but also does not reimplement everything.
"""
name = None
"""The name of the current Enum entry, same as :func:`enum.Enum.name`
"""
value = None
"""The value of the current Enum entry, same as :func:`enum.Enum.value`
"""
# A dict of all values mapping to the entries in the enum
_value_to_entry = {} # type: Dict[str, Any]
@classmethod
def values(cls):
"""Get all the possible values for the enum.
Returns:
list: the list of all possible values for the enum
"""
return cls._value_to_entry.keys()
def __new__(cls, value):
try:
return cls._value_to_entry[value]
except KeyError:
if type(value) is cls: # pylint: disable=unidiomatic-typecheck
return value
raise ValueError("Unknown enum value: {}".format(value))
def __eq__(self, other):
if self.__class__ is not other.__class__:
raise ValueError("Unexpected comparison between {} and {}".format(self, repr(other)))
# Enums instances are unique, so creating an instance with the same value as another will just
# send back the other one, hence we can use an identity comparison, which is much faster than '=='
return self is other
def __ne__(self, other):
if self.__class__ is not other.__class__:
raise ValueError("Unexpected comparison between {} and {}".format(self, repr(other)))
return self is not other
def __hash__(self):
return hash(id(self))
def __str__(self):
return "{}.{}".format(self.__class__.__name__, self.name)
def __reduce__(self):
return self.__class__, (self.value,)
class CoreWarnings:
"""CoreWarnings()
Some common warnings which are raised by core functionalities within BuildStream are found in this class.
"""
OVERLAPS = "overlaps"
"""
This warning will be produced when buildstream detects an overlap on an element
which is not whitelisted. See :ref:`Overlap Whitelist <public_overlap_whitelist>`
"""
UNSTAGED_FILES = "unstaged-files"
"""
This warning will be produced when a file cannot be staged. This can happen when
a file overlaps with a directory in the sandbox that is not empty.
"""
REF_NOT_IN_TRACK = "ref-not-in-track"
"""
This warning will be produced when a source is configured with a reference
which is found to be invalid based on the configured track
"""
UNALIASED_URL = "unaliased-url"
"""
A URL used for fetching a sources was specified without specifying any
:ref:`alias <project_source_aliases>`
"""
class OverlapAction(FastEnum):
"""OverlapAction()
Defines what action to take when files staged into the sandbox overlap.
.. note::
This only dictates what happens when functions such as
:func:`Element.stage_artifact() <buildstream.element.Element.stage_artifact>` and
:func:`Element.stage_dependency_artifacts() <buildstream.element.Element.stage_dependency_artifacts>`
are called multiple times in an Element's :func:`Element.stage() <buildstream.element.Element.stage>`
implementation, and the files staged from one function call result in overlapping files staged
from previous invocations.
If multiple staged elements overlap eachother within a single call to
:func:`Element.stage_dependency_artifacts() <buildstream.element.Element.stage_dependency_artifacts>`,
then the :ref:`overlap whitelist <public_overlap_whitelist>` will be ovserved, and warnings will
be issued for overlapping files, which will be fatal warnings if
:attr:`CoreWarnings.OVERLAPS <buildstream.types.CoreWarnings.OVERLAPS>` is specified
as a :ref:`fatal warning <configurable_warnings>`.
"""
ERROR = "error"
"""
It is an error to overlap previously staged files
"""
WARNING = "warning"
"""
A warning will be issued for previously staged files, which will fatal if
:attr:`CoreWarnings.OVERLAPS <buildstream.types.CoreWarnings.OVERLAPS>` is specified
as a :ref:`fatal warning <configurable_warnings>` in the project.
"""
IGNORE = "ignore"
"""
Overlapping files are acceptable, and do not cause any warning or error.
"""
# _Scope():
#
# Defines the scope of dependencies to include for a given element
# when iterating over the dependency graph in APIs like
# Element._dependencies().
#
class _Scope(FastEnum):
# All elements which the given element depends on, following
# all elements required for building. Including the element itself.
#
ALL = 1
# All elements required for building the element, including their
# respective run dependencies. Not including the given element itself.
#
BUILD = 2
# All elements required for running the element. Including the element
# itself.
#
RUN = 3
# Just the element itself, no dependencies.
#
NONE = 4
# _KeyStrength():
#
# Strength of cache key
#
class _KeyStrength(FastEnum):
# Includes strong cache keys of all build dependencies and their
# runtime dependencies.
STRONG = 1
# Includes names of direct build dependencies but does not include
# cache keys of dependencies.
WEAK = 2
# _DisplayKey():
#
# The components of a cache key which need to be displayed
#
# This is a part of Message() so it needs to be a simple serializable object.
#
# Args:
# full: A full hex digest cache key for an Element
# brief: An abbreviated hex digest cache key for an Element
# strict: Whether the key matches the key which would be used in strict mode
#
class _DisplayKey:
def __init__(self, full: str, brief: str, strict: bool):
self.full = full # type: str
self.brief = brief # type: str
self.strict = strict # type: bool
# _SchedulerErrorAction()
#
# Actions the scheduler can take on error
#
class _SchedulerErrorAction(FastEnum):
# Continue building the rest of the tree
CONTINUE = "continue"
# finish ongoing work and quit
QUIT = "quit"
# Abort immediately
TERMINATE = "terminate"
# _CacheBuildTrees()
#
# When to cache build trees
#
class _CacheBuildTrees(FastEnum):
# Always store build trees
ALWAYS = "always"
# Store build trees when they might be useful for BuildStream
# (eg: on error, to allow for a shell to debug that)
AUTO = "auto"
# Never cache build trees
NEVER = "never"
# _SourceUriPolicy()
#
# A policy for which URIs to access when fetching and tracking
#
class _SourceUriPolicy(FastEnum):
# Use all URIs from default aliases and mirrors
ALL = "all"
# Use only the base source aliases defined in project configuration
#
ALIASES = "aliases"
# Use only URIs from source mirrors (whether they are found
# in project configuration or user configuration)
MIRRORS = "mirrors"
# Use only URIs from user configuration, intentionally causing
# a failure if we try to access a source for which the user
# configuration has not provided a mirror
USER = "user"
# _PipelineSelection()
#
# Defines the kind of pipeline selection to make when the pipeline
# is provided a list of targets, for whichever purpose.
#
# These values correspond to the CLI `--deps` arguments for convenience.
#
class _PipelineSelection(FastEnum):
# Select only the target elements in the associated targets
NONE = "none"
# As NONE, but redirect elements that are capable of it
REDIRECT = "redirect"
# All dependencies of all targets, including the targets
ALL = "all"
# All direct build dependencies and their recursive runtime dependencies,
# excluding the targets
BUILD = "build"
# All direct runtime dependencies and their recursive runtime dependencies,
# including the targets
RUN = "run"
def __str__(self):
return str(self.value)
# _ProjectInformation()
#
# A descriptive object about a project.
#
# Args:
# project (Project): The project instance
# provenance_node (Node): The provenance information, if any
# duplicates (list): List of project descriptions which declared this project as a duplicate
# internal (list): List of project descriptions which declared this project as internal
#
class _ProjectInformation:
def __init__(self, project, provenance_node, duplicates, internal):
self.project = project
self.provenance = provenance_node.get_provenance() if provenance_node else None
self.duplicates = duplicates
self.internal = internal
# _HostMount()
#
# A simple object describing the behavior of a host mount.
#
class _HostMount:
def __init__(self, path: str, host_path: Optional[str] = None, optional: bool = False) -> None:
# Support environment variable expansion in host mounts
path = os.path.expandvars(path)
if host_path is None:
host_path = path
else:
host_path = os.path.expandvars(host_path)
self.path: str = path # Path inside the sandbox
self.host_path: str = host_path # Path on the host
self.optional: bool = optional # Optional mounts do not incur warnings or errors
# _SourceMirror()
#
# A simple object describing a source mirror
#
# Args:
# name: The mirror name
# aliases: A dictionary of URI lists, keyed by alias names
#
class _SourceMirror:
def __init__(self, name: str, aliases: Dict[str, List[str]]):
self.name: str = name
self.aliases: Dict[str, List[str]] = aliases
# new_from_node():
#
# Creates a _SourceMirror() from a YAML loaded node.
#
# Args:
# node: The configuration node describing the spec.
#
# Returns:
# The described _SourceMirror instance.
#
# Raises:
# LoadError: If the node is malformed.
#
@classmethod
def new_from_node(cls, node: MappingNode) -> "_SourceMirror":
node.validate_keys(["name", "aliases"])
name: str = node.get_str("name")
aliases: Dict[str, List[str]] = {}
alias_node: MappingNode = node.get_mapping("aliases")
for alias, uris in alias_node.items():
assert type(uris) is SequenceNode # pylint: disable=unidiomatic-typecheck
aliases[alias] = uris.as_str_list()
return cls(name, aliases)
########################################
# Type aliases #
########################################
# Internal reference for a given Source
SourceRef = Union[None, int, str, List[Any], Dict[str, Any]]
"""
A simple python object used to describe and exact set of sources
This can be ``None`` in order to represent an absense of a source reference,
otherwise it can be ``int``, ``str``, or a complex ``list`` or ``dict`` consisting
of ``int``, ``str``, ``list`` and ``dict`` types.
The order of elements in ``list`` objects is meaningful and should be produced
deterministically by :class:`.Source` implementations, as this order will effect
:ref:`cache keys <cachekeys>`.
See the :ref:`source documentation <core_source_ref>` for more detils on how
:class:`.Source` implementations are expected to handle the source ref.
"""