summaryrefslogtreecommitdiff
path: root/src/buildstream/source.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildstream/source.py')
-rw-r--r--src/buildstream/source.py135
1 files changed, 84 insertions, 51 deletions
diff --git a/src/buildstream/source.py b/src/buildstream/source.py
index f49cdb493..4839cf0fe 100644
--- a/src/buildstream/source.py
+++ b/src/buildstream/source.py
@@ -59,10 +59,6 @@ For loading and configuration purposes, Sources must implement the
Sources expose the following abstract methods. Unless explicitly mentioned,
these methods are mandatory to implement.
-* :func:`Source.get_consistency() <buildstream.source.Source.get_consistency>`
-
- Report the sources consistency state.
-
* :func:`Source.load_ref() <buildstream.source.Source.load_ref>`
Load the ref from a specific YAML node
@@ -169,7 +165,7 @@ from typing import Iterable, Iterator, Optional, Tuple, TYPE_CHECKING
from . import _yaml, utils
from .node import MappingNode
from .plugin import Plugin
-from .types import Consistency, SourceRef, Union, List
+from .types import SourceRef, Union, List
from ._exceptions import BstError, ImplError, PluginError, ErrorDomain
from ._loader.metasource import MetaSource
from ._projectrefs import ProjectRefStorage
@@ -356,7 +352,6 @@ class Source(Plugin):
self.__element_index = meta.element_index # The index of the source in the owning element's source list
self.__element_kind = meta.element_kind # The kind of the element owning this source
self.__directory = meta.directory # Staging relative directory
- self.__consistency = Consistency.INCONSISTENT # Cached consistency state
self.__meta_kind = meta.kind # The kind of this source, required for unpickling
self.__key = None # Cache key for source
@@ -379,6 +374,8 @@ class Source(Plugin):
self._configure(self.__config)
self.__digest = None
+ self.__is_cached = None
+
COMMON_CONFIG_KEYS = ["kind", "directory"]
"""Common source config keys
@@ -389,13 +386,6 @@ class Source(Plugin):
#############################################################
# Abstract Methods #
#############################################################
- def get_consistency(self) -> int:
- """Report whether the source has a resolved reference
-
- Returns:
- (:class:`.Consistency`): The source consistency
- """
- raise ImplError("Source plugin '{}' does not implement get_consistency()".format(self.get_kind()))
def load_ref(self, node: MappingNode) -> None:
"""Loads the *ref* for this Source from the specified *node*.
@@ -560,15 +550,27 @@ class Source(Plugin):
"""Implement any validations once we know the sources are cached
This is guaranteed to be called only once for a given session
- once the sources are known to be
- :attr:`Consistency.CACHED <buildstream.types.Consistency.CACHED>`,
- if source tracking is enabled in the session for this source,
+ once the sources are known to be cached.
+ If source tracking is enabled in the session for this source,
then this will only be called if the sources become cached after
tracking completes.
*Since: 1.4*
"""
+ def is_cached(self) -> bool:
+ """Get whether the source has a local copy of its data.
+
+ This method is guaranteed to only be called whenever
+ :func:`Source.is_resolved() <buildstream.source.Source.is_resolved>`
+ returns `True`.
+
+ Returns: whether the source is cached locally or not.
+
+ *Since: 1.93.0*
+ """
+ raise ImplError("Source plugin '{}' does not implement is_cached()".format(self.get_kind()))
+
#############################################################
# Public Methods #
#############################################################
@@ -705,6 +707,22 @@ class Source(Plugin):
with utils._tempdir(dir=mirrordir) as tempdir:
yield tempdir
+ def is_resolved(self) -> bool:
+ """Get whether the source is resolved.
+
+ This has a default implementation that checks whether the source
+ has a ref or not. If it has a ref, it is assumed to be resolved.
+
+ Sources that never have a ref or have uncommon requirements can
+ override this method to specify when they should be considered
+ resolved
+
+ Returns: whether the source is fully resolved or not
+
+ *Since: 1.93.0*
+ """
+ return self.get_ref() is not None
+
#############################################################
# Private Abstract Methods used in BuildStream #
#############################################################
@@ -752,41 +770,39 @@ class Source(Plugin):
# Prepend provenance to the error
raise SourceError("{}: {}".format(self, e), reason=e.reason) from e
- # Update cached consistency for a source
- #
- # This must be called whenever the state of a source may have changed.
+ # Get whether the source is cached by the source plugin
#
- def _update_state(self):
+ def _is_cached(self):
+ if self.__is_cached is None:
+ # We guarantee we only ever call this when we are resolved.
+ assert self.is_resolved()
- if self.__consistency < Consistency.CACHED:
+ # Set to 'False' on the first call, this prevents throwing multiple errors if the
+ # plugin throws exception when we display the end result pipeline.
+ # Otherwise, the summary would throw a second exception and we would not
+ # have a nice error reporting.
+ self.__is_cached = False
- # Source consistency interrogations are silent.
- context = self._get_context()
- with context.messenger.silence():
- try:
- self.__consistency = self.get_consistency() # pylint: disable=assignment-from-no-return
- except SourceError:
- # SourceErrors should be preserved so that the
- # plugin can communicate real error cases.
- raise
- except Exception as err: # pylint: disable=broad-except
- # Generic errors point to bugs in the plugin, so
- # we need to catch them and make sure they do not
- # cause stacktraces
- raise PluginError(
- "Source plugin '{}' failed to compute source consistency: {}".format(self.get_kind(), err),
- reason="source-bug",
- )
-
- # Give the Source an opportunity to validate the cached
- # sources as soon as the Source becomes Consistency.CACHED.
- if self.__consistency == Consistency.CACHED:
- self.validate_cache()
-
- # Return cached consistency
- #
- def _get_consistency(self):
- return self.__consistency
+ try:
+ self.__is_cached = self.is_cached() # pylint: disable=assignment-from-no-return
+ except SourceError:
+ # SourceErrors should be preserved so that the
+ # plugin can communicate real error cases.
+ raise
+ except Exception as err: # pylint: broad-except
+ # Generic errors point to bugs in the plugin, so
+ # we need to catch them and make sure they do not
+ # cause stacktraces
+
+ raise PluginError(
+ "Source plugin '{}' failed to check its cached state: {}".format(self.get_kind(), err),
+ reason="source-bug",
+ )
+
+ if self.__is_cached:
+ self.validate_cache()
+
+ return self.__is_cached
# Wrapper function around plugin provided fetch method
#
@@ -803,6 +819,24 @@ class Source(Plugin):
else:
self.__do_fetch()
+ self.validate_cache()
+
+ # _fetch_done()
+ #
+ # Indicates that fetching the source has been done.
+ #
+ # Args:
+ # fetched_original (bool): Whether the original sources had been asked (and fetched) or not
+ #
+ def _fetch_done(self, fetched_original):
+ if fetched_original:
+ # The original was fetched, we know we are cached
+ self.__is_cached = True
+ else:
+ # The original was not requested, we might or might not be cached
+ # Don't recompute, but allow recomputation later if needed
+ self.__is_cached = None
+
# Wrapper for stage() api which gives the source
# plugin a fully constructed path considering the
# 'directory' option
@@ -1234,7 +1268,6 @@ class Source(Plugin):
#
clone._preflight()
clone._load_ref()
- clone._update_state()
return clone
@@ -1411,9 +1444,9 @@ class Source(Plugin):
for index, src in enumerate(previous_sources):
# BuildStream should track sources in the order they appear so
# previous sources should never be in an inconsistent state
- assert src.get_consistency() != Consistency.INCONSISTENT
+ assert src.is_resolved()
- if src.get_consistency() == Consistency.RESOLVED:
+ if not src._is_cached():
src._fetch(previous_sources[0:index])