You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildstream.apache.org by no...@apache.org on 2020/12/29 12:30:35 UTC

[buildstream] 03/06: _pipeline.py: Adjust remove_elements to work on the 'intersection'

This is an automated email from the ASF dual-hosted git repository.

not-in-ldap pushed a commit to branch except_intersections
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 0bec3b6828d89c014ba3072173e472237a84d91f
Author: Tristan Maat <tr...@codethink.co.uk>
AuthorDate: Tue Nov 7 18:06:41 2017 +0000

    _pipeline.py: Adjust remove_elements to work on the 'intersection'
---
 buildstream/_pipeline.py | 80 +++++++++++++++++++++++++++++-------------------
 1 file changed, 48 insertions(+), 32 deletions(-)

diff --git a/buildstream/_pipeline.py b/buildstream/_pipeline.py
index f89ad2d..de916f4 100644
--- a/buildstream/_pipeline.py
+++ b/buildstream/_pipeline.py
@@ -25,6 +25,7 @@ import stat
 import shlex
 import shutil
 import tarfile
+import itertools
 from operator import itemgetter
 from tempfile import TemporaryDirectory
 from pluginbase import PluginBase
@@ -236,6 +237,8 @@ class Pipeline():
     # ordering for optimal build plans
     def plan(self):
         build_plan = Planner().plan(self.targets)
+        self.remove_elements(build_plan)
+
         for element in build_plan:
             yield element
 
@@ -710,48 +713,61 @@ class Pipeline():
     #
     # Internal function
     #
-    # Returns all elements to be removed from the given list of
-    # elements when the given removed elements and their unique
-    # dependencies are removed.
+    # Return what we are left with after the intersection between
+    # excepted and target elements and their unique dependencies is
+    # gone.
     #
     # Args:
-    #    elements (list of elements): The graph to sever elements from.
-    #    removed (list of strings): Names of the elements to remove.
-    def remove_elements(self, tree, removed):
+    #    elements (list of elements): The list to remove elements from.
+    def remove_elements(self, elements):
+        targeted = list(self.dependencies(Scope.ALL))
+
+        visited = []
 
-        if removed is None:
-            removed = []
+        def find_intersection(element):
+            if element in visited:
+                return
+            visited.append(element)
 
-        to_remove = set()
-        tree = list(tree)
+            # Intersection elements are those that are also in
+            # 'targeted', as long as we don't recurse into them.
+            if element in targeted:
+                yield element
+            else:
+                for dep in element.dependencies(Scope.ALL, recurse=False):
+                    yield from find_intersection(dep)
 
-        # Find all elements that might need to be removed.
-        def search_tree(element_name):
-            for element in tree:
-                if element.name == element_name:
-                    return element
-            return None
+        # Build a list of 'intersection' elements, i.e. the set of
+        # elements that lie on the border closest to excepted elements
+        # between excepted and target elements.
+        intersection = list(itertools.chain.from_iterable(
+            find_intersection(element) for element in self.exceptions
+        ))
 
-        for element_name in removed:
-            element = search_tree(element_name)
-            if element is None:
-                raise PipelineError("No element named {} in the loaded pipeline".format(element_name))
+        # Now use this set of elements to traverse the targeted
+        # elements, except 'intersection' elements and their unique
+        # dependencies.
+        visited = []
 
-            to_remove.update(element.dependencies(Scope.ALL))
+        def traverse(element):
+            if element in visited or element in intersection:
+                return
+            visited.append(element)
 
-        old_to_remove = set()
-        while old_to_remove != to_remove:
-            old_to_remove = to_remove
+            for dep in element.dependencies(Scope.ALL, recurse=False):
+                traverse(dep)
 
-            # Of these, find all elements that are not a dependency of
-            # elements still in use.
-            for element in tree:
-                if element.name not in removed and element not in to_remove:
-                    to_remove = to_remove.difference(element.dependencies(Scope.ALL, recurse=False))
+        for element in self.targets:
+            traverse(element)
 
-            to_remove = to_remove.union([e for e in tree if e.name in removed])
+        # That looks like a lot, but overall we only traverse (part
+        # of) the graph twice. This could be reduced to once if we
+        # kept track of parent elements, but is probably not
+        # significant.
 
-        return [element for element in tree if element not in to_remove]
+        # Ensure that we return elements in the same order they were
+        # in before.
+        return [element for element in elements if element in visited]
 
     def validate_workspace_index(self, source_index):
         sources = list(self.targets[0].sources())
@@ -787,7 +803,7 @@ class Pipeline():
 
             elements = list(self.dependencies(scope))
 
-        return self.remove_elements(elements, except_)
+        return self.remove_elements(elements)
 
     # source_bundle()
     #