summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan van Berkom <tristan@codethink.co.uk>2020-09-02 16:47:45 +0900
committerTristan van Berkom <tristan@codethink.co.uk>2020-09-03 12:44:10 +0900
commit6a76b1e435e535432f671c1d3cf5d26ce58beecc (patch)
tree7be819d7022bec9fbafb7b1afa751c67e2ff43a5
parent15f5b2d781d6ec48e8636f98d6e80a4004bc76e1 (diff)
downloadbuildstream-6a76b1e435e535432f671c1d3cf5d26ce58beecc.tar.gz
_loader/loadelement.pyx: Merge duplicate dependencies.
When the same element is specified multiple times as a direct dependency, merge and accumulate the results into the already loaded dependency. * If the same element is a runtime and build dependency separately, it will be a single dependency of both runtime and build. * If either of the dependencies are `strict`, it will be a strict dependency. The build graph retains the invariant that an element only ever depends on another element once directly, only the YAML can express the same dependency differently more than once, and the results are accumulated. This consequently remoces LoadErrorReason.DUPLICATE_DEPENDENCY as this is no longer relevant.
-rw-r--r--src/buildstream/_loader/loadelement.pyx52
-rw-r--r--src/buildstream/exceptions.py3
2 files changed, 26 insertions, 29 deletions
diff --git a/src/buildstream/_loader/loadelement.pyx b/src/buildstream/_loader/loadelement.pyx
index 31c54ced4..c7d6ec88d 100644
--- a/src/buildstream/_loader/loadelement.pyx
+++ b/src/buildstream/_loader/loadelement.pyx
@@ -187,6 +187,17 @@ cdef class Dependency:
if not self.junction and ':' in self.name:
self.junction, self.name = self.name.rsplit(':', maxsplit=1)
+ # merge()
+ #
+ # Merge the attributes of an existing dependency into this dependency
+ #
+ # Args:
+ # other (Dependency): The dependency to merge into this one
+ #
+ cdef merge(self, Dependency other):
+ self.dep_type = self.dep_type | other.dep_type
+ self.strict = self.strict or other.strict
+
# LoadElement():
#
@@ -446,12 +457,11 @@ def sort_dependencies(LoadElement element, set visited):
# node (Node): A YAML loaded dictionary
# key (str): the key on the Node corresponding to the dependency type
# default_dep_type (DependencyType): type to give to the dependency
-# acc (list): a list in which to add the loaded dependencies
-# rundeps (dict): a dictionary mapping dependency (junction, name) to dependency for runtime deps
-# builddeps (dict): a dictionary mapping dependency (junction, name) to dependency for build deps
+# acc (dict): a dict in which to add the loaded dependencies
#
-cdef void _extract_depends_from_node(Node node, str key, int default_dep_type, list acc, dict rundeps, dict builddeps) except *:
+cdef void _extract_depends_from_node(Node node, str key, int default_dep_type, dict acc) except *:
cdef SequenceNode depends = node.get_sequence(key, [])
+ cdef Dependency existing_dep
cdef Node dep_node
cdef tuple deptup
@@ -459,21 +469,13 @@ cdef void _extract_depends_from_node(Node node, str key, int default_dep_type, l
dependency = Dependency()
dependency.load(dep_node, default_dep_type)
deptup = (dependency.junction, dependency.name)
- if dependency.dep_type & DependencyType.BUILD:
- if deptup in builddeps:
- raise LoadError("{}: Duplicate build dependency found at {}."
- .format(dependency.provenance, builddeps[deptup].provenance),
- LoadErrorReason.DUPLICATE_DEPENDENCY)
- else:
- builddeps[deptup] = dependency
- if dependency.dep_type & DependencyType.RUNTIME:
- if deptup in rundeps:
- raise LoadError("{}: Duplicate runtime dependency found at {}."
- .format(dependency.provenance, rundeps[deptup].provenance),
- LoadErrorReason.DUPLICATE_DEPENDENCY)
- else:
- rundeps[deptup] = dependency
- acc.append(dependency)
+
+ # Accumulate dependencies, merging any matching elements along the way
+ existing_dep = <Dependency> acc.get(deptup, None)
+ if existing_dep is not None:
+ existing_dep.merge(dependency)
+ else:
+ acc[deptup] = dependency
# Now delete the field, we dont want it anymore
node.safe_del(key)
@@ -494,10 +496,8 @@ cdef void _extract_depends_from_node(Node node, str key, int default_dep_type, l
# (list): a list of Dependency objects
#
def extract_depends_from_node(Node node):
- cdef list acc = []
- cdef dict rundeps = {}
- cdef dict builddeps = {}
- _extract_depends_from_node(node, <str> Symbol.BUILD_DEPENDS, <int> DependencyType.BUILD, acc, rundeps, builddeps)
- _extract_depends_from_node(node, <str> Symbol.RUNTIME_DEPENDS, <int> DependencyType.RUNTIME, acc, rundeps, builddeps)
- _extract_depends_from_node(node, <str> Symbol.DEPENDS, <int> 0, acc, rundeps, builddeps)
- return acc
+ cdef dict acc = {}
+ _extract_depends_from_node(node, <str> Symbol.BUILD_DEPENDS, <int> DependencyType.BUILD, acc)
+ _extract_depends_from_node(node, <str> Symbol.RUNTIME_DEPENDS, <int> DependencyType.RUNTIME, acc)
+ _extract_depends_from_node(node, <str> Symbol.DEPENDS, <int> 0, acc)
+ return [dep for dep in acc.values()]
diff --git a/src/buildstream/exceptions.py b/src/buildstream/exceptions.py
index caf08ae57..0ce1956e0 100644
--- a/src/buildstream/exceptions.py
+++ b/src/buildstream/exceptions.py
@@ -137,9 +137,6 @@ class LoadErrorReason(Enum):
PROTECTED_VARIABLE_REDEFINED = 23
"""An attempt was made to set the value of a protected variable"""
- DUPLICATE_DEPENDENCY = 24
- """A duplicate dependency was detected"""
-
LINK_FORBIDDEN_DEPENDENCIES = 25
"""A link element declared dependencies"""