summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buildstream/_artifact.py2
-rw-r--r--src/buildstream/_frontend/widget.py6
-rw-r--r--src/buildstream/_pipeline.py10
-rw-r--r--src/buildstream/_stream.py2
-rw-r--r--src/buildstream/element.py188
-rw-r--r--src/buildstream/plugins/elements/filter.py11
-rw-r--r--tests/artifactcache/push.py2
7 files changed, 134 insertions, 87 deletions
diff --git a/src/buildstream/_artifact.py b/src/buildstream/_artifact.py
index f74f3f9ff..1df665c14 100644
--- a/src/buildstream/_artifact.py
+++ b/src/buildstream/_artifact.py
@@ -181,7 +181,7 @@ class Artifact:
size += public_data_digest.size_bytes
# store build dependencies
- for e in element.dependencies(Scope.BUILD):
+ for e in element._dependencies(Scope.BUILD):
new_build = artifact.build_deps.add()
new_build.project_name = e.project_name
new_build.element_name = e.name
diff --git a/src/buildstream/_frontend/widget.py b/src/buildstream/_frontend/widget.py
index a4268f62b..dbe6b4337 100644
--- a/src/buildstream/_frontend/widget.py
+++ b/src/buildstream/_frontend/widget.py
@@ -413,17 +413,17 @@ class LogLine(Widget):
# Dependencies
if "%{deps" in format_:
- deps = [e.name for e in element.dependencies(Scope.ALL, recurse=False)]
+ deps = [e.name for e in element._dependencies(Scope.ALL, recurse=False)]
line = p.fmt_subst(line, "deps", yaml.safe_dump(deps, default_style=None).rstrip("\n"))
# Build Dependencies
if "%{build-deps" in format_:
- build_deps = [e.name for e in element.dependencies(Scope.BUILD, recurse=False)]
+ build_deps = [e.name for e in element._dependencies(Scope.BUILD, recurse=False)]
line = p.fmt_subst(line, "build-deps", yaml.safe_dump(build_deps, default_style=False).rstrip("\n"))
# Runtime Dependencies
if "%{runtime-deps" in format_:
- runtime_deps = [e.name for e in element.dependencies(Scope.RUN, recurse=False)]
+ runtime_deps = [e.name for e in element._dependencies(Scope.RUN, recurse=False)]
line = p.fmt_subst(
line, "runtime-deps", yaml.safe_dump(runtime_deps, default_style=False).rstrip("\n")
)
diff --git a/src/buildstream/_pipeline.py b/src/buildstream/_pipeline.py
index 340c12b1a..0fb30e244 100644
--- a/src/buildstream/_pipeline.py
+++ b/src/buildstream/_pipeline.py
@@ -157,7 +157,7 @@ class Pipeline:
visited = (BitMap(), BitMap())
for target in targets:
- for element in target.dependencies(scope, recurse=recurse, visited=visited):
+ for element in target._dependencies(scope, recurse=recurse, visited=visited):
yield element
# plan()
@@ -251,7 +251,7 @@ class Pipeline:
if element in targeted:
yield element
else:
- for dep in element.dependencies(Scope.ALL, recurse=False):
+ for dep in element._dependencies(Scope.ALL, recurse=False):
yield from find_intersection(dep)
# Build a list of 'intersection' elements, i.e. the set of
@@ -272,7 +272,7 @@ class Pipeline:
continue
visited.append(element)
- queue.extend(element.dependencies(Scope.ALL, recurse=False))
+ queue.extend(element._dependencies(Scope.ALL, recurse=False))
# That looks like a lot, but overall we only traverse (part
# of) the graph twice. This could be reduced to once if we
@@ -474,12 +474,12 @@ class _Planner:
return
self.visiting_elements.add(element)
- for dep in element.dependencies(Scope.RUN, recurse=False):
+ for dep in element._dependencies(Scope.RUN, recurse=False):
self.plan_element(dep, depth)
# Dont try to plan builds of elements that are cached already
if not element._cached_success():
- for dep in element.dependencies(Scope.BUILD, recurse=False):
+ for dep in element._dependencies(Scope.BUILD, recurse=False):
self.plan_element(dep, depth + 1)
self.depth_map[element] = depth
diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py
index e9bd60244..4665a05fd 100644
--- a/src/buildstream/_stream.py
+++ b/src/buildstream/_stream.py
@@ -818,7 +818,7 @@ class Stream:
for target in elements:
if not list(target.sources()):
- build_depends = [x.name for x in target.dependencies(Scope.BUILD, recurse=False)]
+ build_depends = [x.name for x in target._dependencies(Scope.BUILD, recurse=False)]
if not build_depends:
raise StreamError("The element {} has no sources".format(target.name))
detail = "Try opening a workspace on one of its dependencies instead:\n"
diff --git a/src/buildstream/element.py b/src/buildstream/element.py
index 4b53aa3dd..3953c7da9 100644
--- a/src/buildstream/element.py
+++ b/src/buildstream/element.py
@@ -105,6 +105,7 @@ from .sandbox._config import SandboxConfig
from .sandbox._sandboxremote import SandboxRemote
from .types import CoreWarnings, Scope, _CacheBuildTrees, _KeyStrength
from ._artifact import Artifact
+from ._elementproxy import ElementProxy
from ._elementsources import ElementSources
from ._loader import Symbol, MetaSource
@@ -435,65 +436,13 @@ class Element(Plugin):
Yields:
The dependencies in `scope`, in deterministic staging order
"""
- # The format of visited is (BitMap(), BitMap()), with the first BitMap
- # containing element that have been visited for the `Scope.BUILD` case
- # and the second one relating to the `Scope.RUN` case.
- if not recurse:
- result: Set[Element] = set()
- if scope in (Scope.BUILD, Scope.ALL):
- for dep in self.__build_dependencies:
- if dep not in result:
- result.add(dep)
- yield dep
- if scope in (Scope.RUN, Scope.ALL):
- for dep in self.__runtime_dependencies:
- if dep not in result:
- result.add(dep)
- yield dep
- else:
-
- def visit(element, scope, visited):
- if scope == Scope.ALL:
- visited[0].add(element._unique_id)
- visited[1].add(element._unique_id)
-
- for dep in chain(element.__build_dependencies, element.__runtime_dependencies):
- if dep._unique_id not in visited[0] and dep._unique_id not in visited[1]:
- yield from visit(dep, Scope.ALL, visited)
-
- yield element
- elif scope == Scope.BUILD:
- visited[0].add(element._unique_id)
-
- for dep in element.__build_dependencies:
- if dep._unique_id not in visited[1]:
- yield from visit(dep, Scope.RUN, visited)
-
- elif scope == Scope.RUN:
- visited[1].add(element._unique_id)
-
- for dep in element.__runtime_dependencies:
- if dep._unique_id not in visited[1]:
- yield from visit(dep, Scope.RUN, visited)
-
- yield element
- else:
- yield element
-
- if visited is None:
- # Visited is of the form (Visited for Scope.BUILD, Visited for Scope.RUN)
- visited = (BitMap(), BitMap())
- else:
- # We have already a visited set passed. we might be able to short-circuit
- if scope in (Scope.BUILD, Scope.ALL) and self._unique_id in visited[0]:
- return
- if scope in (Scope.RUN, Scope.ALL) and self._unique_id in visited[1]:
- return
-
- yield from visit(self, scope, visited)
+ for dep in self._dependencies(scope, recurse=recurse):
+ yield cast("Element", ElementProxy(self, dep))
def search(self, scope: Scope, name: str) -> Optional["Element"]:
- """Search for a dependency by name
+ """search(scope, *, name)
+
+ Search for a dependency by name
Args:
scope: The scope to search
@@ -502,9 +451,9 @@ class Element(Plugin):
Returns:
The dependency element, or None if not found.
"""
- for dep in self.dependencies(scope):
- if dep.name == name:
- return dep
+ search = self._search(scope, name)
+ if search:
+ return cast("Element", ElementProxy(self, search))
return None
@@ -732,7 +681,7 @@ class Element(Plugin):
# The bottom item overlaps nothing
overlapping_elements = elements[1:]
for elm in overlapping_elements:
- element = cast(Element, self.search(scope, elm))
+ element = cast(Element, self._search(scope, elm))
if not element.__file_is_whitelisted(f):
overlap_warning_elements.append(elm)
overlap_warning = True
@@ -877,6 +826,101 @@ class Element(Plugin):
# Private Methods used in BuildStream #
#############################################################
+ # _dependencies()
+ #
+ # A generator function which yields the dependencies of the given element.
+ #
+ # If `recurse` is specified (the default), the full dependencies will be listed
+ # in deterministic staging order, starting with the basemost elements in the
+ # given `scope`. Otherwise, if `recurse` is not specified then only the direct
+ # dependencies in the given `scope` will be traversed, and the element itself
+ # will be omitted.
+ #
+ # Args:
+ # scope (Scope): The scope to iterate in
+ # recurse (bool): Whether to recurse
+ #
+ # Yields:
+ # (Element): The dependencies in `scope`, in deterministic staging order
+ #
+ def _dependencies(self, scope: Scope, *, recurse: bool = True, visited=None) -> Iterator["Element"]:
+
+ # The format of visited is (BitMap(), BitMap()), with the first BitMap
+ # containing element that have been visited for the `Scope.BUILD` case
+ # and the second one relating to the `Scope.RUN` case.
+ if not recurse:
+ result: Set[Element] = set()
+ if scope in (Scope.BUILD, Scope.ALL):
+ for dep in self.__build_dependencies:
+ if dep not in result:
+ result.add(dep)
+ yield dep
+ if scope in (Scope.RUN, Scope.ALL):
+ for dep in self.__runtime_dependencies:
+ if dep not in result:
+ result.add(dep)
+ yield dep
+ else:
+
+ def visit(element, scope, visited):
+ if scope == Scope.ALL:
+ visited[0].add(element._unique_id)
+ visited[1].add(element._unique_id)
+
+ for dep in chain(element.__build_dependencies, element.__runtime_dependencies):
+ if dep._unique_id not in visited[0] and dep._unique_id not in visited[1]:
+ yield from visit(dep, Scope.ALL, visited)
+
+ yield element
+ elif scope == Scope.BUILD:
+ visited[0].add(element._unique_id)
+
+ for dep in element.__build_dependencies:
+ if dep._unique_id not in visited[1]:
+ yield from visit(dep, Scope.RUN, visited)
+
+ elif scope == Scope.RUN:
+ visited[1].add(element._unique_id)
+
+ for dep in element.__runtime_dependencies:
+ if dep._unique_id not in visited[1]:
+ yield from visit(dep, Scope.RUN, visited)
+
+ yield element
+ else:
+ yield element
+
+ if visited is None:
+ # Visited is of the form (Visited for Scope.BUILD, Visited for Scope.RUN)
+ visited = (BitMap(), BitMap())
+ else:
+ # We have already a visited set passed. we might be able to short-circuit
+ if scope in (Scope.BUILD, Scope.ALL) and self._unique_id in visited[0]:
+ return
+ if scope in (Scope.RUN, Scope.ALL) and self._unique_id in visited[1]:
+ return
+
+ yield from visit(self, scope, visited)
+
+ # _search()
+ #
+ # Search for a dependency by name
+ #
+ # Args:
+ # scope (Scope): The scope to search
+ # name (str): The dependency to search for
+ #
+ # Returns:
+ # (Element): The dependency element, or None if not found.
+ #
+ def _search(self, scope: Scope, name: str) -> Optional["Element"]:
+
+ for dep in self._dependencies(scope):
+ if dep.name == name:
+ return dep
+
+ return None
+
# _new_from_load_element():
#
# Recursively instantiate a new Element instance, its sources
@@ -1354,7 +1398,7 @@ class Element(Plugin):
self.__required = True
# Request artifacts of runtime dependencies
- for dep in self.dependencies(Scope.RUN, recurse=False):
+ for dep in self._dependencies(Scope.RUN, recurse=False):
dep._set_required()
# When an element becomes required, it must be assembled for
@@ -1389,7 +1433,7 @@ class Element(Plugin):
self.__artifact_files_required = True
# Request artifact files of runtime dependencies
- for dep in self.dependencies(scope, recurse=False):
+ for dep in self._dependencies(scope, recurse=False):
dep._set_artifact_files_required(scope=scope)
# _artifact_files_required():
@@ -1442,7 +1486,7 @@ class Element(Plugin):
self.__assemble_scheduled = True
# Requests artifacts of build dependencies
- for dep in self.dependencies(Scope.BUILD, recurse=False):
+ for dep in self._dependencies(Scope.BUILD, recurse=False):
dep._set_required()
# Once we schedule an element for assembly, we know that our
@@ -2269,7 +2313,7 @@ class Element(Plugin):
def __get_dependency_artifact_names(self):
return [
os.path.join(dep.project_name, _get_normal_name(dep.name), dep._get_cache_key())
- for dep in self.dependencies(Scope.BUILD)
+ for dep in self._dependencies(Scope.BUILD)
]
# __get_last_build_artifact()
@@ -2333,21 +2377,23 @@ class Element(Plugin):
def __preflight(self):
if self.BST_FORBID_RDEPENDS and self.BST_FORBID_BDEPENDS:
- if any(self.dependencies(Scope.RUN, recurse=False)) or any(self.dependencies(Scope.BUILD, recurse=False)):
+ if any(self._dependencies(Scope.RUN, recurse=False)) or any(
+ self._dependencies(Scope.BUILD, recurse=False)
+ ):
raise ElementError(
"{}: Dependencies are forbidden for '{}' elements".format(self, self.get_kind()),
reason="element-forbidden-depends",
)
if self.BST_FORBID_RDEPENDS:
- if any(self.dependencies(Scope.RUN, recurse=False)):
+ if any(self._dependencies(Scope.RUN, recurse=False)):
raise ElementError(
"{}: Runtime dependencies are forbidden for '{}' elements".format(self, self.get_kind()),
reason="element-forbidden-rdepends",
)
if self.BST_FORBID_BDEPENDS:
- if any(self.dependencies(Scope.BUILD, recurse=False)):
+ if any(self._dependencies(Scope.BUILD, recurse=False)):
raise ElementError(
"{}: Build dependencies are forbidden for '{}' elements".format(self, self.get_kind()),
reason="element-forbidden-bdepends",
@@ -2904,7 +2950,7 @@ class Element(Plugin):
[e.project_name, e.name, e._get_cache_key(strength=_KeyStrength.WEAK)]
if self.BST_STRICT_REBUILD or e in self.__strict_dependencies
else [e.project_name, e.name]
- for e in self.dependencies(Scope.BUILD)
+ for e in self._dependencies(Scope.BUILD)
]
self.__weak_cache_key = self._calculate_cache_key(dependencies)
@@ -2917,7 +2963,7 @@ class Element(Plugin):
if self.__strict_cache_key is None:
dependencies = [
[e.project_name, e.name, e.__strict_cache_key] if e.__strict_cache_key is not None else None
- for e in self.dependencies(Scope.BUILD)
+ for e in self._dependencies(Scope.BUILD)
]
self.__strict_cache_key = self._calculate_cache_key(dependencies)
@@ -3007,7 +3053,7 @@ class Element(Plugin):
self.__cache_key = strong_key
elif self.__assemble_scheduled or self.__assemble_done:
# Artifact will or has been built, not downloaded
- dependencies = [[e.project_name, e.name, e._get_cache_key()] for e in self.dependencies(Scope.BUILD)]
+ dependencies = [[e.project_name, e.name, e._get_cache_key()] for e in self._dependencies(Scope.BUILD)]
self.__cache_key = self._calculate_cache_key(dependencies)
if self.__cache_key is None:
diff --git a/src/buildstream/plugins/elements/filter.py b/src/buildstream/plugins/elements/filter.py
index 62c04e027..9647c7191 100644
--- a/src/buildstream/plugins/elements/filter.py
+++ b/src/buildstream/plugins/elements/filter.py
@@ -169,7 +169,7 @@ class FilterElement(Element):
def preflight(self):
# Exactly one build-depend is permitted
- build_deps = list(self.dependencies(Scope.BUILD, recurse=False))
+ build_deps = list(self._dependencies(Scope.BUILD, recurse=False))
if len(build_deps) != 1:
detail = "Full list of build-depends:\n"
deps_list = " \n".join([x.name for x in build_deps])
@@ -183,7 +183,7 @@ class FilterElement(Element):
)
# That build-depend must not also be a runtime-depend
- runtime_deps = list(self.dependencies(Scope.RUN, recurse=False))
+ runtime_deps = list(self._dependencies(Scope.RUN, recurse=False))
if build_deps[0] in runtime_deps:
detail = "Full list of runtime depends:\n"
deps_list = " \n".join([x.name for x in runtime_deps])
@@ -222,7 +222,7 @@ class FilterElement(Element):
def assemble(self, sandbox):
with self.timed_activity("Staging artifact", silent_nested=True):
- for dep in self.dependencies(Scope.BUILD, recurse=False):
+ for dep in self._dependencies(Scope.BUILD, recurse=False):
# Check that all the included/excluded domains exist
pub_data = dep.get_public_data("bst")
split_rules = pub_data.get_mapping("split-rules", {})
@@ -253,14 +253,15 @@ class FilterElement(Element):
def _get_source_element(self):
# Filter elements act as proxies for their sole build-dependency
- build_deps = list(self.dependencies(Scope.BUILD, recurse=False))
+ #
+ build_deps = list(self._dependencies(Scope.BUILD, recurse=False))
assert len(build_deps) == 1
output_elm = build_deps[0]._get_source_element()
return output_elm
def integrate(self, sandbox):
if self.pass_integration:
- for dep in self.dependencies(Scope.BUILD, recurse=False):
+ for dep in self._dependencies(Scope.BUILD, recurse=False):
dep.integrate(sandbox)
super().integrate(sandbox)
diff --git a/tests/artifactcache/push.py b/tests/artifactcache/push.py
index 07f2c2560..87bc3b325 100644
--- a/tests/artifactcache/push.py
+++ b/tests/artifactcache/push.py
@@ -35,7 +35,7 @@ def _push(cli, cache_dir, project_dir, config_file, target):
# Ensure the element's artifact memeber is initialised
# This is duplicated from Pipeline.resolve_elements()
# as this test does not use the cli frontend.
- for e in element.dependencies(Scope.ALL):
+ for e in element._dependencies(Scope.ALL):
e._initialize_state()
# Manually setup the CAS remotes