From 930c42cde1f129485a6186365db5f10a47ae43a5 Mon Sep 17 00:00:00 2001 From: memsharded Date: Sat, 8 Jun 2024 23:28:00 +0200 Subject: [PATCH 1/3] explore replace_requires references adjustements --- conans/client/graph/graph.py | 1 + conans/client/graph/graph_builder.py | 1 + conans/model/dependencies.py | 6 ++++ .../graph/test_replace_requires.py | 34 +++++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/conans/client/graph/graph.py b/conans/client/graph/graph.py index 74a7f0273e9..4e268e1f5fc 100644 --- a/conans/client/graph/graph.py +++ b/conans/client/graph/graph.py @@ -68,6 +68,7 @@ def __init__(self, ref, conanfile, context, recipe=None, path=None, test=False): self.should_build = False # If the --build or policy wants to build this binary self.build_allowed = False self.is_conf = False + self.replaced_requires = {} # To track the replaced requires for self.dependencies[old-ref] def __lt__(self, other): """ diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index 41793dee9e6..a442fa4a135 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -312,6 +312,7 @@ def _resolve_replace_requires(self, node, require, profile_build, profile_host, node.conanfile.requires.reindex(require, alternative_ref.name) require.ref.name = alternative_ref.name graph.replaced_requires[original_require] = repr(require.ref) + node.replaced_requires[original_require] = require break # First match executes the alternative and finishes checking others def _create_new_node(self, node, require, graph, profile_host, profile_build, graph_lock): diff --git a/conans/model/dependencies.py b/conans/model/dependencies.py index 0f8640fc8e4..48053bf70d0 100644 --- a/conans/model/dependencies.py +++ b/conans/model/dependencies.py @@ -91,6 +91,12 @@ class ConanFileDependencies(UserRequirementsDict): def from_node(node): d = OrderedDict((require, ConanFileInterface(transitive.node.conanfile)) for require, transitive in node.transitive_deps.items()) + for old_req, new_req in node.replaced_requires.items(): + existing = d.get(new_req) + if existing is not None: + added_req = new_req.copy_requirement() + added_req.ref = RecipeReference.loads(old_req) + d[added_req] = existing return ConanFileDependencies(d) def filter(self, require_filter, remove_system=True): diff --git a/test/integration/graph/test_replace_requires.py b/test/integration/graph/test_replace_requires.py index 3d2c385ed03..a14ebf3c469 100644 --- a/test/integration/graph/test_replace_requires.py +++ b/test/integration/graph/test_replace_requires.py @@ -1,4 +1,5 @@ import json +import textwrap import pytest @@ -147,3 +148,36 @@ def test_replace_requires_json_format(): assert "pkg/0.1: pkg/0.2" in c.out # The replacement happens graph = json.loads(c.stdout) assert graph["graph"]["replaced_requires"] == {"pkg/0.1": "pkg/0.2"} + + +def test_replace_requires_consumer_references(): + c = TestClient(light=True) + conanfile = textwrap.dedent(""" + from conan import ConanFile + class App(ConanFile): + name = "app" + version = "0.1" + requires = "zlib/0.1" + def generate(self): + assert self.dependencies["zlib"] is self.dependencies["zlib-ng"] + self.output.info(f"DEP ZLIB generate: {self.dependencies['zlib'].ref.name}!") + def build(self): + assert self.dependencies["zlib"] is self.dependencies["zlib-ng"] + self.output.info(f"DEP ZLIB build: {self.dependencies['zlib'].ref.name}!") + def package_info(self): + assert self.dependencies["zlib"] is self.dependencies["zlib-ng"] + # self.cpp_info.requires = ["zlib::zlib"] + """) + c.save({"zlibng/conanfile.py": GenConanfile("zlib-ng", "0.1"), + "app/conanfile.py": conanfile, + "profile": "[replace_requires]\nzlib/0.1: zlib-ng/0.1"}) + c.run("create zlibng") + c.run("build app -pr=profile") + assert "zlib/0.1: zlib-ng/0.1" in c.out + assert "conanfile.py (app/0.1): DEP ZLIB generate: zlib-ng!" in c.out + assert "conanfile.py (app/0.1): DEP ZLIB build: zlib-ng!" in c.out + c.run("create app -pr=profile") + assert "zlib/0.1: zlib-ng/0.1" in c.out + assert "conanfile.py (app/0.1): DEP ZLIB generate: zlib-ng!" in c.out + assert "conanfile.py (app/0.1): DEP ZLIB build: zlib-ng!" in c.out + # FIXME: The self.cpp_info.requires is still broken From 3ea567787218be597f88f631086b58abd7795c2b Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 2 Sep 2024 12:52:08 +0200 Subject: [PATCH 2/3] fix tests --- test/unittests/tools/cmake/test_cmaketoolchain.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/unittests/tools/cmake/test_cmaketoolchain.py b/test/unittests/tools/cmake/test_cmaketoolchain.py index 0420540f73a..2a2c5303615 100644 --- a/test/unittests/tools/cmake/test_cmaketoolchain.py +++ b/test/unittests/tools/cmake/test_cmaketoolchain.py @@ -32,6 +32,7 @@ def conanfile(): c.folders.set_base_generators("/some/abs/path") # non-existing to not relativize c._conan_node = Mock() c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} return c @@ -229,6 +230,8 @@ def conanfile_apple(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + # FIXME: Repeated fix pattern + c._conan_node.replaced_requires = {} return c @@ -258,6 +261,7 @@ def conanfile_msvc(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} return c @@ -332,6 +336,7 @@ def test_older_msvc_toolset(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} toolchain = CMakeToolchain(c) content = toolchain.content assert 'CMAKE_GENERATOR_TOOLSET "v110"' in content @@ -360,6 +365,7 @@ def test_older_msvc_toolset_update(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} toolchain = CMakeToolchain(c) content = toolchain.content assert 'CMAKE_GENERATOR_TOOLSET "v142,version=14.29"' in content @@ -385,6 +391,7 @@ def test_msvc_xp_toolsets(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} toolchain = CMakeToolchain(c) content = toolchain.content assert 'CMAKE_GENERATOR_TOOLSET "v110_xp"' in content @@ -411,6 +418,7 @@ def conanfile_linux(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} return c @@ -442,6 +450,7 @@ def conanfile_linux_shared(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} return c @@ -482,6 +491,7 @@ def conanfile_windows_fpic(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} return c @@ -513,6 +523,7 @@ def conanfile_linux_fpic(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} return c @@ -547,6 +558,7 @@ def test_libcxx_abi_flag(): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} toolchain = CMakeToolchain(c) content = toolchain.content @@ -597,6 +609,7 @@ def test_apple_cmake_osx_sysroot(os, os_sdk, arch, expected_sdk): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} toolchain = CMakeToolchain(c) content = toolchain.content @@ -680,6 +693,7 @@ def conanfile_cross(self): c._conan_node = Mock() c._conan_node.dependencies = [] c._conan_node.transitive_deps = {} + c._conan_node.replaced_requires = {} return c def test_cmake_system_name(self, conanfile_cross): From 887e0d306ed108003f4f6cd9de42dafb40b65d45 Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 24 Sep 2024 19:03:51 +0200 Subject: [PATCH 3/3] new test with requires components and options --- .../graph/test_replace_requires.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/integration/graph/test_replace_requires.py b/test/integration/graph/test_replace_requires.py index d74b885e733..909854d0beb 100644 --- a/test/integration/graph/test_replace_requires.py +++ b/test/integration/graph/test_replace_requires.py @@ -207,3 +207,71 @@ def package_info(self): assert "zlib/0.1: zlib-ng/0.1" in c.out assert "app/0.1: DEP ZLIB generate: zlib-ng!" in c.out assert "app/0.1: DEP ZLIB build: zlib-ng!" in c.out + + +def test_replace_requires_consumer_components_options(): + c = TestClient() + # IMPORTANT: The replacement package must be target-compatible + zlib_ng = textwrap.dedent(""" + from conan import ConanFile + class ZlibNG(ConanFile): + name = "zlib-ng" + version = "0.1" + options = {"compat": [False, True]} + default_options = {"compat": False} + def package_info(self): + self.cpp_info.set_property("cmake_file_name", "ZLIB") + self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB") + if self.options.compat: + self.cpp_info.components["myzlib"].set_property("cmake_target_name", + "ZLIB::zmylib") + """) + conanfile = textwrap.dedent(""" + from conan import ConanFile + class App(ConanFile): + name = "app" + version = "0.1" + settings = "build_type" + requires = "zlib/0.1" + generators = "CMakeDeps" + + def generate(self): + self.output.info(f"DEP ZLIB generate: {self.dependencies['zlib'].ref.name}!") + def build(self): + self.output.info(f"DEP ZLIB build: {self.dependencies['zlib'].ref.name}!") + def package_info(self): + self.output.info(f"DEP ZLIB package_info: {self.dependencies['zlib'].ref.name}!") + self.cpp_info.requires = ["zlib::myzlib"] + """) + profile = textwrap.dedent(""" + [options] + zlib-ng/*:compat=True + + [replace_requires] + zlib/0.1: zlib-ng/0.1 + """) + c.save({"zlibng/conanfile.py": zlib_ng, + "app/conanfile.py": conanfile, + "profile": profile}) + + c.run("create zlibng -o *:compat=True") + c.run("build app -pr=profile") + assert "zlib/0.1: zlib-ng/0.1" in c.out + assert "conanfile.py (app/0.1): DEP ZLIB generate: zlib-ng!" in c.out + assert "conanfile.py (app/0.1): DEP ZLIB build: zlib-ng!" in c.out + # Check generated CMake code. If the targets are NOT compatible, then the replacement + # Cannot happen + assert "find_package(ZLIB)" in c.out + assert "target_link_libraries(... ZLIB::ZLIB)" in c.out + cmake = c.load("app/ZLIBTargets.cmake") + assert "add_library(ZLIB::ZLIB INTERFACE IMPORTED)" in cmake + cmake = c.load("app/ZLIB-Target-none.cmake") + assert "set_property(TARGET ZLIB::ZLIB APPEND PROPERTY INTERFACE_LINK_LIBRARIES ZLIB::zmylib)" \ + in cmake + + c.run("create app -pr=profile") + assert "zlib/0.1: zlib-ng/0.1" in c.out + assert "app/0.1: DEP ZLIB generate: zlib-ng!" in c.out + assert "app/0.1: DEP ZLIB build: zlib-ng!" in c.out + assert "find_package(ZLIB)" in c.out + assert "target_link_libraries(... ZLIB::ZLIB)" in c.out