Repository: conan-io/conan
Pull request #16235 (2024-05-10 13:18:47)

fix lockfile-overrides-bug
Changelog: Bugfix: Solve bug with ``overrides`` from ``lockfiles`` in case of diamond structures
Docs: Omit

Close https://github.com/conan-io/conan/issues/16116



Patch:
diff --git a/conan/tools/cmake/presets.py b/conan/tools/cmake/presets.py
index d50bfdea2..b7e616150 100644
--- a/conan/tools/cmake/presets.py
+++ b/conan/tools/cmake/presets.py
@@ -16,11 +16,12 @@ from conans.util.files import save, load
 
 def write_cmake_presets(conanfile, toolchain_file, generator, cache_variables,
                         user_presets_path=None, preset_prefix=None, buildenv=None, runenv=None,
-                        cmake_executable=None):
+                        cmake_executable=None, absolute_paths=None):
     preset_path, preset_data = _CMakePresets.generate(conanfile, toolchain_file, generator,
                                                       cache_variables, preset_prefix, buildenv, runenv,
-                                                      cmake_executable)
-    _IncludingPresets.generate(conanfile, preset_path, user_presets_path, preset_prefix, preset_data)
+                                                      cmake_executable, absolute_paths)
+    _IncludingPresets.generate(conanfile, preset_path, user_presets_path, preset_prefix, preset_data,
+                               absolute_paths)
 
 
 class _CMakePresets:
@@ -28,12 +29,13 @@ class _CMakePresets:
     """
     @staticmethod
     def generate(conanfile, toolchain_file, generator, cache_variables, preset_prefix, buildenv, runenv,
-                 cmake_executable):
+                 cmake_executable, absolute_paths):
         toolchain_file = os.path.abspath(os.path.join(conanfile.generators_folder, toolchain_file))
-        try:  # Make it relative to the build dir if possible
-            toolchain_file = os.path.relpath(toolchain_file, conanfile.build_folder)
-        except ValueError:
-            pass
+        if not absolute_paths:
+            try:  # Make it relative to the build dir if possible
+                toolchain_file = os.path.relpath(toolchain_file, conanfile.build_folder)
+            except ValueError:
+                pass
         cache_variables = cache_variables or {}
         if platform.system() == "Windows" and generator == "MinGW Makefiles":
             if "CMAKE_SH" not in cache_variables:
@@ -249,7 +251,8 @@ class _IncludingPresets:
     """
 
     @staticmethod
-    def generate(conanfile, preset_path, user_presets_path, preset_prefix, preset_data):
+    def generate(conanfile, preset_path, user_presets_path, preset_prefix, preset_data,
+                 absolute_paths):
         if not user_presets_path:
             return
 
@@ -288,10 +291,11 @@ class _IncludingPresets:
         if inherited_user:
             _IncludingPresets._clean_user_inherits(data, preset_data)
 
-        try:  # Make it relative to the CMakeUserPresets.json if possible
-            preset_path = os.path.relpath(preset_path, output_dir)
-        except ValueError:
-            pass
+        if not absolute_paths:
+            try:  # Make it relative to the CMakeUserPresets.json if possible
+                preset_path = os.path.relpath(preset_path, output_dir)
+            except ValueError:
+                pass
         data = _IncludingPresets._append_user_preset_path(data, preset_path, output_dir)
 
         data = json.dumps(data, indent=4)
diff --git a/conan/tools/cmake/toolchain/toolchain.py b/conan/tools/cmake/toolchain/toolchain.py
index 22e8c3801..549e2c0b3 100644
--- a/conan/tools/cmake/toolchain/toolchain.py
+++ b/conan/tools/cmake/toolchain/toolchain.py
@@ -179,6 +179,7 @@ class CMakeToolchain(object):
         self.presets_prefix = "conan"
         self.presets_build_environment = None
         self.presets_run_environment = None
+        self.absolute_paths = False  # By default use relative paths to toolchain and presets
 
     def _context(self):
         """ Returns dict, the context for the template
@@ -268,7 +269,7 @@ class CMakeToolchain(object):
 
         write_cmake_presets(self._conanfile, toolchain_file, self.generator, cache_variables,
                             self.user_presets_path, self.presets_prefix, buildenv, runenv,
-                            cmake_executable)
+                            cmake_executable, self.absolute_paths)
 
     def _get_generator(self, recipe_generator):
         # Returns the name of the generator to be used by CMake
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index 129ceb76f..bf64917c7 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -94,7 +94,7 @@ compiler:
                     "10", "10.1", "10.2", "10.3", "10.4", "10.5",
                     "11", "11.1", "11.2", "11.3", "11.4",
                     "12", "12.1", "12.2", "12.3",
-                    "13", "13.1", "13.2",
+                    "13", "13.1", "13.2", "13.3",
                     "14", "14.1"]
         libcxx: [libstdc++, libstdc++11]
         threads: [null, posix, win32]  # Windows MinGW
diff --git a/conans/client/graph/graph.py b/conans/client/graph/graph.py
index f6f1ba5cb..56463b2b5 100644
--- a/conans/client/graph/graph.py
+++ b/conans/client/graph/graph.py
@@ -97,6 +97,11 @@ class Node(object):
                 # print("  +++++Runtime conflict!", require, "with", node.ref)
                 return True
             require.aggregate(existing.require)
+            # An override can be overriden by a downstream force/override
+            if existing.require.override and existing.require.ref != require.ref:
+                # If it is an override, but other value, it has been overriden too
+                existing.require.overriden_ref = existing.require.ref
+                existing.require.override_ref = require.ref
 
         assert not require.version_range  # No ranges slip into transitive_deps definitions
         # TODO: Might need to move to an update() for performance
@@ -118,6 +123,7 @@ class Node(object):
         if down_require is None:
             return
 
+        down_require.defining_require = require.defining_require
         return d.src.propagate_downstream(down_require, node)
 
     def check_downstream_exists(self, require):
@@ -161,6 +167,7 @@ class Node(object):
             # print("    No need to check downstream more")
             return result
 
+        down_require.defining_require = require.defining_require
         source_node = dependant.src
         return source_node.check_downstream_exists(down_require) or result
 
@@ -275,9 +282,9 @@ class Overrides:
         overrides = {}
         for n in nodes:
             for r in n.conanfile.requires.values():
-                if r.override:
+                if r.override and not r.overriden_ref:  # overrides are not real graph edges
                     continue
-                if r.overriden_ref and not r.force:
+                if r.overriden_ref:
                     overrides.setdefault(r.overriden_ref, set()).add(r.override_ref)
                 else:
                     overrides.setdefault(r.ref, set()).add(None)
diff --git a/conans/client/graph/graph_binaries.py b/conans/client/graph/graph_binaries.py
index 6663bca70..46ef40517 100644
--- a/conans/client/graph/graph_binaries.py
+++ b/conans/client/graph/graph_binaries.py
@@ -150,7 +150,7 @@ class GraphBinariesAnalyzer(object):
             if node.binary in (BINARY_CACHE, BINARY_UPDATE, BINARY_DOWNLOAD):
                 _compatible_found(package_id, compatible_package)
                 return
-        if not update:
+        if not should_update_reference(conanfile.ref, update):
             conanfile.output.info(f"Compatible configurations not found in cache, checking servers")
             for package_id, compatible_package in compatibles.items():
                 conanfile.output.info(f"'{package_id}': "
diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py
index 937d32f76..64c9a4bad 100644
--- a/conans/client/graph/graph_builder.py
+++ b/conans/client/graph/graph_builder.py
@@ -81,9 +81,13 @@ class DepsGraphBuilder(object):
 
             prev_ref = prev_node.ref if prev_node else prev_require.ref
             if prev_require.force or prev_require.override:  # override
-                require.overriden_ref = require.ref  # Store that the require has been overriden
-                require.override_ref = prev_ref
-                require.ref = prev_ref
+                if prev_require.defining_require is not require:
+                    require.overriden_ref = require.overriden_ref or require.ref.copy()  # Old one
+                    # require.override_ref can be !=None if lockfile-overrides defined
+                    require.override_ref = (require.override_ref or prev_require.override_ref
+                                            or prev_require.ref.copy())  # New one
+                    require.defining_require = prev_require.defining_require  # The overrider
+                require.ref = prev_ref  # New one, maybe resolved with revision
             else:
                 self._conflicting_version(require, node, prev_require, prev_node,
                                           prev_ref, base_previous, self._resolve_prereleases)
@@ -195,6 +199,8 @@ class DepsGraphBuilder(object):
                 if not resolved:
                     self._resolve_alias(node, require, alias, graph)
             self._resolve_replace_requires(node, require, profile_build, profile_host, graph)
+            if graph_lock:
+                graph_lock.resolve_overrides(require)
             node.transitive_deps[require] = TransitiveRequirement(require, node=None)
 
     def _resolve_alias(self, node, require, alias, graph):
diff --git a/conans/client/graph/install_graph.py b/conans/client/graph/install_graph.py
index 2471e1f62..51cec4fcb 100644
--- a/conans/client/graph/install_graph.py
+++ b/conans/client/graph/install_graph.py
@@ -160,7 +160,7 @@ class _InstallRecipeReference:
             if dep.dst.binary != BINARY_SKIP:
                 if dep.dst.ref == node.ref:  # If the node is itself, then it is internal dep
                     install_pkg_ref.depends.append(dep.dst.pref.package_id)
-                else:
+                elif dep.dst.ref not in self.depends:
                     self.depends.append(dep.dst.ref)
 
     def _install_order(self):
diff --git a/conans/model/graph_lock.py b/conans/model/graph_lock.py
index 2ac43ac58..29e87de77 100644
--- a/conans/model/graph_lock.py
+++ b/conans/model/graph_lock.py
@@ -261,7 +261,6 @@ class Lockfile(object):
             locked_refs = self._conf_requires.refs()
         else:
             locked_refs = self._requires.refs()
-        self._resolve_overrides(require)
         try:
             self._resolve(require, locked_refs, resolve_prereleases)
         except ConanException:
@@ -273,13 +272,19 @@ class Lockfile(object):
                 ConanOutput().error(msg, error_type="exception")
             raise
 
-    def _resolve_overrides(self, require):
-        existing = self._overrides.get(require.ref)
-        if existing is not None and len(existing) == 1:
-            require.overriden_ref = require.ref  # Store that the require has been overriden
-            ref = next(iter(existing))
-            require.ref = ref
-            require.override_ref = ref
+    def resolve_overrides(self, require):
+        """ The lockfile contains the overrides to be able to inject them when the lockfile is
+        applied to upstream dependencies, that have the overrides downstream
+        """
+        if not self._overrides:
+            return
+
+        overriden = self._overrides.get(require.ref)
+        if overriden and len(overriden) == 1:
+            override_ref = next(iter(overriden))
+            require.overriden_ref = require.overriden_ref or require.ref.copy()
+            require.override_ref = override_ref
+            require.ref = override_ref
 
     def resolve_prev(self, node):
         if node.context == CONTEXT_BUILD:
diff --git a/conans/model/recipe_ref.py b/conans/model/recipe_ref.py
index ebbe5c189..01168f09f 100644
--- a/conans/model/recipe_ref.py
+++ b/conans/model/recipe_ref.py
@@ -26,6 +26,11 @@ class RecipeReference:
         self.revision = revision
         self.timestamp = timestamp
 
+    def copy(self):
+        # Used for creating copy in lockfile-overrides mechanism
+        return RecipeReference(self.name, self.version, self.user, self.channel, self.revision,
+                               self.timestamp)
+
     def __repr__(self):
         """ long repr like pkg/0.1@user/channel#rrev%timestamp """
         result = self.repr_notime()
diff --git a/conans/model/requires.py b/conans/model/requires.py
index 428bb5968..23987d0dc 100644
--- a/conans/model/requires.py
+++ b/conans/model/requires.py
@@ -29,6 +29,9 @@ class Requirement:
         self._direct = direct
         self.options = options
         # Meta and auxiliary information
+        # The "defining_require" is the require that defines the current value. If this require is
+        # overriden/forced, this attribute will point to the overriding/forcing requirement.
+        self.defining_require = self  # if not overriden, it points to itself
         self.overriden_ref = None  # to store if the requirement has been overriden (store old ref)
         self.override_ref = None  # to store if the requirement has been overriden (store new ref)
         self.is_test = test  # to store that it was a test, even if used as regular requires too
@@ -518,16 +521,6 @@ class Requirements:
             raise ConanException("Duplicated requirement: {}".format(ref))
         self._requires[req] = req
 
-    def override(self, ref):
-        req = Requirement(ref)
-        old_requirement = self._requires.get(req)
-        if old_requirement is not None:
-            req.force = True
-            self._requires[req] = req
-        else:
-            req.override = True
-            self._requires[req] = req
-
     def test_require(self, ref, run=None, options=None, force=None):
         """
              Represent a testing framework like gtest


Test patch:
diff --git a/conans/test/integration/graph/conflict_diamond_test.py b/conans/test/integration/graph/conflict_diamond_test.py
index 462bfecdd..86d4320e7 100644
--- a/conans/test/integration/graph/conflict_diamond_test.py
+++ b/conans/test/integration/graph/conflict_diamond_test.py
@@ -54,7 +54,7 @@ class TestConflictDiamondTest:
             c.save({"game/conanfile.py": _game_conanfile(v)})
             c.run("install game")
             c.assert_overrides({"math/1.0": [f"math/{v}"],
-                                "math/1.0.1": [f"math/{v}#8e1a7a5ce869d8c54ae3d33468fd657c"]})
+                                "math/1.0.1": [f"math/{v}"]})
             c.assert_listed_require({f"math/{v}": "Cache"})
 
         # Check that order of requirements doesn't affect
@@ -62,7 +62,7 @@ class TestConflictDiamondTest:
             c.save({"game/conanfile.py": _game_conanfile(v, reverse=True)})
             c.run("install game")
             c.assert_overrides({"math/1.0": [f"math/{v}"],
-                                "math/1.0.1": [f"math/{v}#8e1a7a5ce869d8c54ae3d33468fd657c"]})
+                                "math/1.0.1": [f"math/{v}"]})
             c.assert_listed_require({f"math/{v}": "Cache"})
 
         c.run("install --requires=engine/1.0  --requires=ai/1.0", assert_error=True)
diff --git a/conans/test/integration/graph/test_test_requires.py b/conans/test/integration/graph/test_test_requires.py
index c5531422f..18ce5bd32 100644
--- a/conans/test/integration/graph/test_test_requires.py
+++ b/conans/test/integration/graph/test_test_requires.py
@@ -106,7 +106,7 @@ class TestTestRequiresDiamond:
         c.run("create rapidcheck")
         c.run("install game")
         c.assert_listed_require({"gtest/1.0": "Cache"}, test=True)
-        c.assert_overrides({"gtest/1.1": ["gtest/1.0#08dd14dd79315dd95c94b85d13bd1388"]})
+        c.assert_overrides({"gtest/1.1": ["gtest/1.0"]})
 
 
 def test_require_options():
diff --git a/conans/test/integration/graph/version_ranges/version_range_override_test.py b/conans/test/integration/graph/version_ranges/version_range_override_test.py
index 52587d45a..30bbf2d7b 100644
--- a/conans/test/integration/graph/version_ranges/version_range_override_test.py
+++ b/conans/test/integration/graph/version_ranges/version_range_override_test.py
@@ -104,7 +104,7 @@ class TestVersionRangeOverrideFail:
                 "ros_core/pr-53@3rdparty/snapshot"
             ],
             "ros_core/1.1.4@3rdparty/unstable": [
-                "ros_core/pr-53@3rdparty/snapshot#4d670581ccb765839f2239cc8dff8fbd"
+                "ros_core/pr-53@3rdparty/snapshot"
             ]
         }
         assert info['graph']["overrides"] == expected_overrides
diff --git a/conans/test/integration/lockfile/test_ci_overrides.py b/conans/test/integration/lockfile/test_ci_overrides.py
new file mode 100644
index 000000000..e587988df
--- /dev/null
+++ b/conans/test/integration/lockfile/test_ci_overrides.py
@@ -0,0 +1,389 @@
+import json
+
+import pytest
+
+from conans.model.recipe_ref import RecipeReference
+from conans.test.assets.genconanfile import GenConanfile
+from conans.test.utils.tools import TestClient
+
+
+def test_graph_build_order_override_error():
+    """
+    libc -> libb -> liba -> zlib/1.2
+              |--------------/
+      |-----override------> zlib/1.3
+    """
+    c = TestClient()
+    c.save({"zlib/conanfile.py": GenConanfile("zlib"),
+            "liba/conanfile.py": GenConanfile("liba", "0.1").with_requires("zlib/1.0"),
+            "libb/conanfile.py": GenConanfile("libb", "0.1").with_requires("liba/0.1", "zlib/2.0"),
+            "libc/conanfile.py": GenConanfile("libc", "0.1").with_requirement("libb/0.1")
+                                                            .with_requirement("zlib/3.0",
+                                                                              override=True)
+            })
+    c.run("export zlib --version=2.0")
+    c.run("export zlib --version=3.0")
+    c.run("export liba")
+    c.run("export libb")
+    c.run("export libc")
+    c.run("graph info --requires=libc/0.1 --lockfile-out=output.lock")
+
+    c.run("graph build-order --requires=libc/0.1 --lockfile=output.lock --order-by=configuration "
+          "--build=missing --format=json")
+
+    to_build = json.loads(c.stdout)
+    for level in to_build["order"]:
+        for package in level:
+            binary = package["binary"]
+            if binary != "Build":
+                continue
+            build_args = package["build_args"]
+            c.run(f"install {build_args} --lockfile=output.lock")
+            ref = RecipeReference.loads(package["ref"])
+            assert f"{ref}: Building from source"
+
+    c.run("install --requires=libc/0.1 --lockfile=output.lock")
+    # All works, all binaries exist now
+    assert "zlib/3.0: Already installed!" in c.out
+    assert "liba/0.1: Already installed!" in c.out
+    assert "libb/0.1: Already installed!" in c.out
+    assert "libc/0.1: Already installed!" in c.out
+
+
+@pytest.mark.parametrize("replace_pattern", ["*", "3.0"])
+def test_graph_build_order_override_replace_requires(replace_pattern):
+    """
+    libc -> libb -> liba -> zlib/1.2
+              |--------------/
+      |-----override------> zlib/1.3
+
+    replace_requires zlib -> zlib/system
+    """
+    c = TestClient()
+    c.save({"zlib/conanfile.py": GenConanfile("zlib"),
+            "liba/conanfile.py": GenConanfile("liba", "0.1").with_requires("zlib/1.0"),
+            "libb/conanfile.py": GenConanfile("libb", "0.1").with_requires("liba/0.1", "zlib/2.0"),
+            "libc/conanfile.py": GenConanfile("libc", "0.1").with_requirement("libb/0.1")
+                                                            .with_requirement("zlib/3.0",
+                                                                              override=True),
+            "profile": f"[replace_requires]\nzlib/{replace_pattern}: zlib/system"
+            })
+    c.run("export zlib --version=2.0")
+    c.run("export zlib --version=3.0")
+    c.run("export zlib --version=system")
+    c.run("export liba")
+    c.run("export libb")
+    c.run("export libc")
+    c.run("lock create --requires=libc/0.1 --lockfile-out=output.lock -pr=profile")
+
+    c.run("graph build-order --requires=libc/0.1 --lockfile=output.lock --order-by=configuration "
+          "--build=missing -pr=profile --format=json")
+
+    to_build = json.loads(c.stdout)
+    for level in to_build["order"]:
+        for package in level:
+            binary = package["binary"]
+            if binary != "Build":
+                continue
+            build_args = package["build_args"]
+            c.run(f"install {build_args} --lockfile=output.lock -pr=profile")
+            ref = RecipeReference.loads(package["ref"])
+            assert f"{ref}: Building from source"
+
+    c.run("install --requires=libc/0.1 --lockfile=output.lock -pr=profile")
+    # All works, all binaries exist now
+    assert "zlib/system: Already installed!" in c.out
+    assert "liba/0.1: Already installed!" in c.out
+    assert "libb/0.1: Already installed!" in c.out
+    assert "libc/0.1: Already installed!" in c.out
+
+
+def test_single_config_decentralized_overrides():
+    r""" same scenario as "test_single_config_centralized()", but distributing the build in
+    different build servers, using the "build-order"
+    Now with overrides
+
+    pkga -> toola/1.0 -> toolb/1.0 -> toolc/1.0
+                \------override-----> toolc/2.0
+    pkgb -> toola/2.0 -> toolb/1.0 -> toolc/1.0
+                \------override-----> toolc/3.0
+    pkgc -> toola/3.0 -> toolb/1.0 -> toolc/1.0
+    """
+    c = TestClient()
+    c.save({"toolc/conanfile.py": GenConanfile("toolc"),
+            "toolb/conanfile.py": GenConanfile("toolb").with_requires("toolc/1.0"),
+            "toola/conanfile.py": GenConanfile("toola", "1.0").with_requirement("toolb/1.0")
+                                                              .with_requirement("toolc/2.0",
+                                                                                override=True),
+            "toola2/conanfile.py": GenConanfile("toola", "2.0").with_requirement("toolb/1.0")
+                                                               .with_requirement("toolc/3.0",
+                                                                                 override=True),
+            "toola3/conanfile.py": GenConanfile("toola", "3.0").with_requirement("toolb/1.0"),
+            "pkga/conanfile.py": GenConanfile("pkga", "1.0").with_tool_requires("toola/1.0"),
+            "pkgb/conanfile.py": GenConanfile("pkgb", "1.0").with_requires("pkga/1.0")
+                                                            .with_tool_requires("toola/2.0"),
+            "pkgc/conanfile.py": GenConanfile("pkgc", "1.0").with_requires("pkgb/1.0")
+                                                            .with_tool_requires("toola/3.0"),
+            })
+    c.run("export toolc --version=1.0")
+    c.run("export toolc --version=2.0")
+    c.run("export toolc --version=3.0")
+
+    c.run("export toolb --version=1.0")
+
+    c.run("export toola")
+    c.run("export toola2")
+    c.run("export toola3")
+
+    c.run("export pkga")
+    c.run("export pkgb")
+    c.run("lock create pkgc")
+    lock = json.loads(c.load("pkgc/conan.lock"))
+    requires = "\n".join(lock["build_requires"])
+    assert "toolc/3.0" in requires
+    assert "toolc/2.0" in requires
+    assert "toolc/1.0" in requires
+    assert len(lock["overrides"]) == 1
+    assert set(lock["overrides"]["toolc/1.0"]) == {"toolc/3.0", "toolc/2.0", None}
+
+    c.run("graph build-order pkgc --lockfile=pkgc/conan.lock --format=json --build=missing")
+    to_build = json.loads(c.stdout)
+    for level in to_build:
+        for elem in level:
+            for package in elem["packages"][0]:  # assumes no dependencies between packages
+                binary = package["binary"]
+                if binary != "Build":
+                    continue
+                build_args = package["build_args"]
+                c.run(f"install {build_args} --lockfile=pkgc/conan.lock")
+
+    c.run("install pkgc --lockfile=pkgc/conan.lock")
+    # All works, all binaries exist now
+    assert "pkga/1.0: Already installed!" in c.out
+    assert "pkgb/1.0: Already installed!" in c.out
+
+
+def test_single_config_decentralized_overrides_nested():
+    r""" same scenario as "test_single_config_centralized()", but distributing the build in
+    different build servers, using the "build-order"
+    Now with overrides
+
+    pkga -> toola/1.0 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
+               \                          \-----------override---------> libf/2.0
+                \--------------------override--------------------------> libf/3.0
+    """
+    c = TestClient()
+    c.save({"libf/conanfile.py": GenConanfile("libf"),
+            "libe/conanfile.py": GenConanfile("libe", "1.0").with_requires("libf/1.0"),
+            "libd/conanfile.py": GenConanfile("libd", "1.0").with_requires("libe/1.0"),
+            "libc/conanfile.py": GenConanfile("libc", "1.0").with_requirement("libd/1.0")
+                                                            .with_requirement("libf/2.0",
+                                                                              override=True),
+            "libb/conanfile.py": GenConanfile("libb", "1.0").with_requires("libc/1.0"),
+            "toola/conanfile.py": GenConanfile("toola", "1.0").with_requirement("libb/1.0")
+                                                              .with_requirement("libf/3.0",
+                                                                                override=True),
+            "pkga/conanfile.py": GenConanfile("pkga", "1.0").with_tool_requires("toola/1.0"),
+            })
+
+    c.run("export libf --version=3.0")
+    c.run("export libe")
+    c.run("export libd")
+    c.run("export libc")
+    c.run("export libb")
+    c.run("export toola")
+
+    c.run("lock create pkga")
+    lock = json.loads(c.load("pkga/conan.lock"))
+    assert lock["overrides"] == {"libf/1.0": ["libf/3.0"],
+                                 "libf/2.0": ["libf/3.0"]}
+
+    c.run("graph build-order pkga --lockfile=pkga/conan.lock --format=json --build=missing")
+    to_build = json.loads(c.stdout)
+    for level in to_build:
+        for elem in level:
+            ref = elem["ref"]
+            if "libc" in ref:
+                pass
+            for package in elem["packages"][0]:  # assumes no dependencies between packages
+                binary = package["binary"]
+                if binary != "Build":
+                    continue
+                build_args = package["build_args"]
+                c.run(f"install {build_args} --lockfile=pkga/conan.lock")
+
+    c.run("install pkga --lockfile=pkga/conan.lock")
+    # All works, all binaries exist now
+    assert "Install finished successfully" in c.out
+
+
+@pytest.mark.parametrize("forced", [False, True])
+def test_single_config_decentralized_overrides_multi(forced):
+    r""" same scenario as "test_single_config_centralized()", but distributing the build in
+    different build servers, using the "build-order"
+    Now with overrides
+
+    pkga -> toola/1.0 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
+      |         \                          \-----------override--------> libf/2.0
+      |          \--------------------override-------------------------> libf/3.0
+    pkgb -> toola/1.1 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
+      |         \                          \-----------override--------> libf/2.0
+      |          \--------------------override-------------------------> libf/4.0
+    pkgc -> toola/1.2 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
+                                           \-----------override--------> libf/2.0
+    """
+    override, force = (True, False) if not forced else (False, True)
+    c = TestClient()
+    c.save({"libf/conanfile.py": GenConanfile("libf"),
+            "libe/conanfile.py": GenConanfile("libe", "1.0").with_requires("libf/1.0"),
+            "libd/conanfile.py": GenConanfile("libd", "1.0").with_requires("libe/1.0"),
+            "libc/conanfile.py": GenConanfile("libc", "1.0").with_requirement("libd/1.0")
+                                                            .with_requirement("libf/2.0",
+                                                                              override=override,
+                                                                              force=force),
+            "libb/conanfile.py": GenConanfile("libb", "1.0").with_requires("libc/1.0"),
+            "toola/conanfile.py": GenConanfile("toola", "1.0").with_requirement("libb/1.0")
+                                                              .with_requirement("libf/3.0",
+                                                                                override=override,
+                                                                                force=force),
+            "toola1/conanfile.py": GenConanfile("toola", "1.1").with_requirement("libb/1.0")
+                                                               .with_requirement("libf/4.0",
+                                                                                 override=override,
+                                                                                 force=force),
+            "toola2/conanfile.py": GenConanfile("toola", "1.2").with_requirement("libb/1.0"),
+            "pkga/conanfile.py": GenConanfile("pkga", "1.0").with_tool_requires("toola/1.0"),
+            "pkgb/conanfile.py": GenConanfile("pkgb", "1.0").with_requires("pkga/1.0")
+                                                            .with_tool_requires("toola/1.1"),
+            "pkgc/conanfile.py": GenConanfile("pkgc", "1.0").with_requires("pkgb/1.0")
+                                                            .with_tool_requires("toola/1.2"),
+            })
+
+    c.run("export libf --version=2.0")
+    c.run("export libf --version=3.0")
+    c.run("export libf --version=4.0")
+    c.run("export libe")
+    c.run("export libd")
+    c.run("export libc")
+    c.run("export libb")
+
+    c.run("export toola")
+    c.run("export toola1")
+    c.run("export toola2")
+
+    c.run("export pkga")
+    c.run("export pkgb")
+    c.run("lock create pkgc")
+    lock = json.loads(c.load("pkgc/conan.lock"))
+    assert len(lock["overrides"]) == 2
+    assert set(lock["overrides"]["libf/1.0"]) == {"libf/4.0", "libf/2.0", "libf/3.0"}
+
+    if forced:  # When forced, there is one libf/2.0 that is not overriden
+        assert set(lock["overrides"]["libf/2.0"]) == {"libf/4.0", "libf/3.0", None}
+    else:
+        assert set(lock["overrides"]["libf/2.0"]) == {"libf/4.0", "libf/3.0"}
+
+    c.run("graph build-order pkgc --lockfile=pkgc/conan.lock --format=json --build=missing")
+    to_build = json.loads(c.stdout)
+    for level in to_build:
+        for elem in level:
+            ref = elem["ref"]
+            if "libc" in ref:
+                pass
+            for package in elem["packages"][0]:  # assumes no dependencies between packages
+                binary = package["binary"]
+                if binary != "Build":
+                    continue
+                build_args = package["build_args"]
+                c.run(f"install {build_args} --lockfile=pkgc/conan.lock")
+
+    c.run("install pkgc --lockfile=pkgc/conan.lock")
+    # All works, all binaries exist now
+    assert "pkga/1.0: Already installed!" in c.out
+    assert "pkgb/1.0: Already installed!" in c.out
+
+
+@pytest.mark.parametrize("replace_pattern", ["*", "1.0", "2.0", "3.0", "4.0"])
+@pytest.mark.parametrize("forced", [False, True])
+def test_single_config_decentralized_overrides_multi_replace_requires(replace_pattern, forced):
+    r""" same scenario as "test_single_config_centralized()", but distributing the build in
+    different build servers, using the "build-order"
+    Now with overrides
+
+    pkga -> toola/1.0 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
+      |         \                          \-----------override--------> libf/2.0
+      |          \--------------------override-------------------------> libf/3.0
+    pkgb -> toola/1.1 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
+      |         \                          \-----------override--------> libf/2.0
+      |          \--------------------override-------------------------> libf/4.0
+    pkgc -> toola/1.2 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
+                                           \-----------override--------> libf/2.0
+    """
+    override, force = (True, False) if not forced else (False, True)
+    c = TestClient()
+    c.save({"libf/conanfile.py": GenConanfile("libf"),
+            "libe/conanfile.py": GenConanfile("libe", "1.0").with_requires("libf/1.0"),
+            "libd/conanfile.py": GenConanfile("libd", "1.0").with_requires("libe/1.0"),
+            "libc/conanfile.py": GenConanfile("libc", "1.0").with_requirement("libd/1.0")
+                                                            .with_requirement("libf/2.0",
+                                                                              override=override,
+                                                                              force=force),
+            "libb/conanfile.py": GenConanfile("libb", "1.0").with_requires("libc/1.0"),
+            "toola/conanfile.py": GenConanfile("toola", "1.0").with_requirement("libb/1.0")
+                                                              .with_requirement("libf/3.0",
+                                                                                override=override,
+                                                                                force=force),
+            "toola1/conanfile.py": GenConanfile("toola", "1.1").with_requirement("libb/1.0")
+                                                               .with_requirement("libf/4.0",
+                                                                                 override=override,
+                                                                                 force=force),
+            "toola2/conanfile.py": GenConanfile("toola", "1.2").with_requirement("libb/1.0"),
+            "pkga/conanfile.py": GenConanfile("pkga", "1.0").with_tool_requires("toola/1.0"),
+            "pkgb/conanfile.py": GenConanfile("pkgb", "1.0").with_requires("pkga/1.0")
+                                                            .with_tool_requires("toola/1.1"),
+            "pkgc/conanfile.py": GenConanfile("pkgc", "1.0").with_requires("pkgb/1.0")
+                                                            .with_tool_requires("toola/1.2"),
+            "profile": f"include(default)\n[replace_requires]\nlibf/{replace_pattern}: libf/system"
+            })
+
+    c.run("export libf --version=2.0")
+    c.run("export libf --version=3.0")
+    c.run("export libf --version=4.0")
+    c.run("export libf --version=system")
+    c.run("export libe")
+    c.run("export libd")
+    c.run("export libc")
+    c.run("export libb")
+
+    c.run("export toola")
+    c.run("export toola1")
+    c.run("export toola2")
+
+    c.run("export pkga")
+    c.run("export pkgb")
+    c.run("lock create pkgc -pr:b=profile")
+
+    # overrides will be different everytime, just checking that things can be built
+    c.run("graph build-order pkgc --lockfile=pkgc/conan.lock --format=json -pr:b=profile "
+          "--build=missing")
+
+    to_build = json.loads(c.stdout)
+    for level in to_build:
+        for elem in level:
+            ref = elem["ref"]
+            if "libc" in ref:
+                pass
+            for package in elem["packages"][0]:  # assumes no dependencies between packages
+                binary = package["binary"]
+                if binary != "Build":
+                    continue
+                build_args = package["build_args"]
+                c.run(f"install {build_args} --lockfile=pkgc/conan.lock -pr:b=profile")
+
+    c.run("install pkgc --lockfile=pkgc/conan.lock -pr:b=profile")
+    # All works, all binaries exist now
+    assert "pkga/1.0: Already installed!" in c.out
+    assert "pkgb/1.0: Already installed!" in c.out
+    if replace_pattern == "1.0":  # These will overriden by downstream
+        assert "libf/system#7fb6d926dabeb955bcea1cafedf953c8 - Cache" not in c.out
+    else:
+        assert "libf/system#7fb6d926dabeb955bcea1cafedf953c8 - Cache" in c.out
diff --git a/conans/test/integration/lockfile/test_graph_overrides.py b/conans/test/integration/lockfile/test_graph_overrides.py
index 6c64ecb1a..4c42a9c67 100644
--- a/conans/test/integration/lockfile/test_graph_overrides.py
+++ b/conans/test/integration/lockfile/test_graph_overrides.py
@@ -134,7 +134,7 @@ def test_overrides_diamond(override, force):
     overrides = json_graph['graph']["overrides"]
     assert len(overrides) == 2
     assert overrides['pkga/0.1'] == ['pkga/0.3']
-    assert overrides['pkga/0.2'] == ['pkga/0.3#e3afb975277bc245212d6e8de88f4f8f']
+    assert overrides['pkga/0.2'] == ['pkga/0.3']
 
     # apply the lockfile to pkgb, should it lock to pkga/0.3
     c.run("graph info pkgb --lockfile=pkgd/conan.lock --format=json")
diff --git a/conans/test/integration/package_id/compatible_test.py b/conans/test/integration/package_id/compatible_test.py
index 782fe7ca2..8e4e0a999 100644
--- a/conans/test/integration/package_id/compatible_test.py
+++ b/conans/test/integration/package_id/compatible_test.py
@@ -71,14 +71,17 @@ class CompatibleIDsTest(unittest.TestCase):
         client.run("export . --name=pkg --version=0.1 --user=user --channel=stable")
         self.assertIn("pkg/0.1@user/stable: Exported: "
                       "pkg/0.1@user/stable#d165eb4bcdd1c894a97d2a212956f5fe", client.out)
+        client.run("export . --name=lib --version=0.1 --user=user --channel=stable")
 
         # package can be used with a profile gcc 4.9 falling back to 4.8 binary
-        client.save({"conanfile.py": GenConanfile().with_require("pkg/0.1@user/stable")})
+        client.save({"conanfile.py": GenConanfile().with_requires("pkg/0.1@user/stable", "lib/0.1@user/stable")})
         # No fallback
-        client.run("install . -pr=myprofile --build=missing")
+        client.run("install . -pr=myprofile --build=missing -u=lib")
         self.assertIn("pkg/0.1@user/stable: PackageInfo!: Gcc version: 4.9!", client.out)
         client.assert_listed_binary({"pkg/0.1@user/stable":
                                      ("1ded27c9546219fbd04d4440e05b2298f8230047", "Build")})
+        assert "lib/0.1@user/stable: Compatible configurations not found in cache, checking servers" not in client.out
+        assert "pkg/0.1@user/stable: Compatible configurations not found in cache, checking servers" in client.out
 
     def test_compatible_setting_no_user_channel(self):
         client = TestClient()
diff --git a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
index ebaf41649..032081f95 100644
--- a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
+++ b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
@@ -1414,3 +1414,27 @@ def test_toolchain_and_compilers_build_context():
     })
     client.run("export tool")
     client.run("create consumer -pr:h host -pr:b build --build=missing")
+
+
+def test_toolchain_keep_absolute_paths():
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.cmake import CMakeToolchain, cmake_layout
+        class Pkg(ConanFile):
+            settings = "build_type"
+            def generate(self):
+                tc = CMakeToolchain(self)
+                tc.absolute_paths = True
+                tc.generate()
+            def layout(self):
+                cmake_layout(self)
+        """)
+    c.save({"conanfile.py": conanfile,
+            "CMakeLists.txt": ""})
+    c.run('install . ')
+
+    user_presets = json.loads(c.load("CMakeUserPresets.json"))
+    assert os.path.isabs(user_presets["include"][0])
+    presets = json.loads(c.load(user_presets["include"][0]))
+    assert os.path.isabs(presets["configurePresets"][0]["toolchainFile"])