summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbst-marge-bot <marge-bot@buildstream.build>2019-05-30 11:25:51 +0000
committerbst-marge-bot <marge-bot@buildstream.build>2019-05-30 11:25:51 +0000
commit02483728b310cac9b8f687b57f061adfe783f99c (patch)
tree16d5eb7e5f92c61ef0433c17a74f13d51a87d60a
parent8b302c55d2da4f7ab29e346496a8e2fe3d7a7660 (diff)
parent10901ea4f7eadb3820a2af2a0f8492671cebb4b3 (diff)
downloadbuildstream-02483728b310cac9b8f687b57f061adfe783f99c.tar.gz
Merge branch 'danielsilverstone-ct/iterative-circdeps' into 'master'
Rewrite `Loader._check_circular_deps()` to be iterative See merge request BuildStream/buildstream!1364
-rw-r--r--src/buildstream/_loader/loader.py73
1 files changed, 39 insertions, 34 deletions
diff --git a/src/buildstream/_loader/loader.py b/src/buildstream/_loader/loader.py
index ca058608d..5a1819e32 100644
--- a/src/buildstream/_loader/loader.py
+++ b/src/buildstream/_loader/loader.py
@@ -338,40 +338,45 @@ class Loader():
# Raises:
# (LoadError): In case there was a circular dependency error
#
- def _check_circular_deps(self, element, check_elements=None, validated=None, sequence=None):
-
- if check_elements is None:
- check_elements = set()
- if validated is None:
- validated = set()
- if sequence is None:
- sequence = []
-
- # Skip already validated branches
- if element in validated:
- return
-
- if element in check_elements:
- # Create `chain`, the loop of element dependencies from this
- # element back to itself, by trimming everything before this
- # element from the sequence under consideration.
- chain = sequence[sequence.index(element.full_name):]
- chain.append(element.full_name)
- raise LoadError(LoadErrorReason.CIRCULAR_DEPENDENCY,
- ("Circular dependency detected at element: {}\n" +
- "Dependency chain: {}")
- .format(element.full_name, " -> ".join(chain)))
-
- # Push / Check each dependency / Pop
- check_elements.add(element)
- sequence.append(element.full_name)
- for dep in element.dependencies:
- dep.element._loader._check_circular_deps(dep.element, check_elements, validated, sequence)
- check_elements.remove(element)
- sequence.pop()
-
- # Eliminate duplicate paths
- validated.add(element)
+ @staticmethod
+ def _check_circular_deps(top_element):
+
+ sequence = [top_element]
+ sequence_indices = [0]
+ check_elements = set(sequence)
+ validated = set()
+
+ while sequence:
+ this_element = sequence[-1]
+ index = sequence_indices[-1]
+ if index < len(this_element.dependencies):
+ element = this_element.dependencies[index].element
+ sequence_indices[-1] = index + 1
+ if element in check_elements:
+ # Create `chain`, the loop of element dependencies from this
+ # element back to itself, by trimming everything before this
+ # element from the sequence under consideration.
+ chain = [element.full_name for element in sequence[sequence.index(element):]]
+ chain.append(element.full_name)
+ raise LoadError(LoadErrorReason.CIRCULAR_DEPENDENCY,
+ ("Circular dependency detected at element: {}\n" +
+ "Dependency chain: {}")
+ .format(element.full_name, " -> ".join(chain)))
+ if element not in validated:
+ # We've not already validated this element, so let's
+ # descend into it to check it out
+ sequence.append(element)
+ sequence_indices.append(0)
+ check_elements.add(element)
+ # Otherwise we'll head back around the loop to validate the
+ # next dependency in this entry
+ else:
+ # Done with entry, pop it off, indicate we're no longer
+ # in its chain, and mark it valid
+ sequence.pop()
+ sequence_indices.pop()
+ check_elements.remove(this_element)
+ validated.add(this_element)
# _sort_dependencies():
#