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

juergbi pushed a commit to branch jbilleter/links
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 0dc98c02c0890f4c25e6aab9f54a77b2fe61bcd8
Author: Jürg Billeter <[email protected]>
AuthorDate: Fri Nov 21 15:09:52 2025 +0100

    _loader: Merge duplicate dependencies via link elements
    
    Duplicate dependencies are normally merged already in
    `extract_depends_from_node()`. However, that only works for dependencies
    that are declared with the same filename. If a link element is in a
    dependency list, merging may still be required but can be done only
    after the link element has been loaded.
    
    The element state updates don't work correctly if there are duplicate
    dependencies that aren't merged.
    
    Fixes #2002.
---
 src/buildstream/_loader/loadelement.pyx |  2 +-
 src/buildstream/_loader/loader.py       | 15 ++++++++++++---
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/buildstream/_loader/loadelement.pyx 
b/src/buildstream/_loader/loadelement.pyx
index 52e9a8ecd..f4ebb673c 100644
--- a/src/buildstream/_loader/loadelement.pyx
+++ b/src/buildstream/_loader/loadelement.pyx
@@ -202,7 +202,7 @@ cdef class Dependency:
     # Args:
     #    other (Dependency): The dependency to merge into this one
     #
-    cdef merge(self, Dependency other):
+    cpdef merge(self, Dependency other):
         self.dep_type = self.dep_type | other.dep_type
         self.strict = self.strict or other.strict
 
diff --git a/src/buildstream/_loader/loader.py 
b/src/buildstream/_loader/loader.py
index 42a7eb4a4..1e4843242 100644
--- a/src/buildstream/_loader/loader.py
+++ b/src/buildstream/_loader/loader.py
@@ -495,7 +495,8 @@ class Loader:
         # The loader queue is a stack of tuples
         # [0] is the LoadElement instance
         # [1] is a stack of Dependency objects to load
-        loader_queue = [(top_element, list(reversed(dependencies)))]
+        # [2] is a Dict[LoadElement, Dependency] of loaded dependencies
+        loader_queue = [(top_element, list(reversed(dependencies)), {})]
 
         # Load all dependency files for the new LoadElement
         while loader_queue:
@@ -526,7 +527,7 @@ class Loader:
                         dep_element.mark_fully_loaded()
 
                         dep_deps = extract_depends_from_node(dep_element.node)
-                        loader_queue.append((dep_element, 
list(reversed(dep_deps))))
+                        loader_queue.append((dep_element, 
list(reversed(dep_deps), {})))
 
                         # Pylint is not very happy about Cython and can't 
understand 'node' is a 'MappingNode'
                         if dep_element.node.get_str(Symbol.KIND) == 
"junction":  # pylint: disable=no-member
@@ -539,7 +540,15 @@ class Loader:
                 # LoadElement on the dependency and append the dependency to 
the owning
                 # LoadElement dependency list.
                 dep.set_element(dep_element)
-                current_element[0].dependencies.append(dep)  # pylint: 
disable=no-member
+
+                dep_dict = current_element[2]
+                if dep.element in dep_dict:
+                    # Duplicate LoadElement in dependency list, this can 
happen if a dependency is
+                    # a link element that points to an element that is already 
a dependency.
+                    dep_dict[dep.element].merge(dep)
+                else:
+                    current_element[0].dependencies.append(dep)  # pylint: 
disable=no-member
+                    dep_dict[dep.element] = dep
             else:
                 # And pop the element off the queue
                 loader_queue.pop()

Reply via email to