diff options
author | bst-marge-bot <marge-bot@buildstream.build> | 2019-05-30 11:25:51 +0000 |
---|---|---|
committer | bst-marge-bot <marge-bot@buildstream.build> | 2019-05-30 11:25:51 +0000 |
commit | 02483728b310cac9b8f687b57f061adfe783f99c (patch) | |
tree | 16d5eb7e5f92c61ef0433c17a74f13d51a87d60a | |
parent | 8b302c55d2da4f7ab29e346496a8e2fe3d7a7660 (diff) | |
parent | 10901ea4f7eadb3820a2af2a0f8492671cebb4b3 (diff) | |
download | buildstream-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.py | 73 |
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(): # |