Skip to content

Commit

Permalink
Fix [replace_requires] for replacements of same reference name (#17409)
Browse files Browse the repository at this point in the history
* Fix [replace_requires] for replacements of same reference name

* review

* ci

---------

Co-authored-by: memsharded <james@conan.io>
  • Loading branch information
AbrilRBS and memsharded authored Dec 4, 2024
1 parent fb965e9 commit 26caf37
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/linux-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
pull_request:
branches:
- '*'
- 'release/*'
workflow_dispatch:

concurrency:
Expand Down Expand Up @@ -69,7 +70,7 @@ jobs:
matrix:
python-version: ['3.12.3', '3.9.2', '3.8.6', '3.6.15']
test-type: ['unittests', 'integration', 'functional']
name: Conan ${{ matrix.test-type }} (${{ matrix.python-version }})
name: Conan ${{ matrix.test-type }} (${{ matrix.python-version }})
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/osx-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
pull_request:
branches:
- '*'
- 'release/*'

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/win-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
pull_request:
branches:
- '*'
- 'release/*'
workflow_dispatch:

concurrency:
Expand Down
2 changes: 1 addition & 1 deletion conan/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from conans.model.conan_file import ConanFile
from conan.tools.scm import Version as _Version

__version__ = '2.10.0'
__version__ = '2.10.1'
conan_version = _Version(__version__)
6 changes: 5 additions & 1 deletion conans/model/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,19 @@ def from_node(node):
d = OrderedDict((require, ConanFileInterface(transitive.node.conanfile))
for require, transitive in node.transitive_deps.items())
if node.replaced_requires:
cant_be_removed = set()
for old_req, new_req in node.replaced_requires.items():
# Two different replaced_requires can point to the same real requirement
existing = d[new_req]
added_req = new_req.copy_requirement()
added_req.ref = RecipeReference.loads(old_req)
d[added_req] = existing
if new_req.ref.name == added_req.ref.name:
cant_be_removed.add(new_req)
# Now remove the replaced from dependencies dict
for new_req in node.replaced_requires.values():
d.pop(new_req, None)
if new_req not in cant_be_removed:
d.pop(new_req, None)
return ConanFileDependencies(d)

def filter(self, require_filter, remove_system=True):
Expand Down
84 changes: 71 additions & 13 deletions test/integration/graph/test_replace_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
("dep/[>=1.0 <2]", "dep/2.1", "dep/2.1@system", "dep/1.1"),
# PATTERN - PATTERN REPLACE
("dep/[>=1.3 <2]", "dep/*", "dep/[>=1.0 <1.9]", "dep/1.1"),
# DIRECT REPLACE OF PINNED VERSIONS
("dep/1.3", "dep/1.3", "dep/1.5", "dep/1.5"),
])
@pytest.mark.parametrize("tool_require", [False, True])
class TestReplaceRequires:
Expand Down Expand Up @@ -160,14 +162,16 @@ def test_replace_requires_test_requires():
assert "gtest/0.1: gtest/0.2" in c.out # The replacement happens


def test_replace_requires_consumer_references():
# We test even replacing by itself, not great, but shouldn't crash
@pytest.mark.parametrize("name, version", [("zlib", "0.1"), ("zlib", "0.2"), ("zlib-ng", "0.1")])
def test_replace_requires_consumer_references(name, version):
c = TestClient()
# IMPORTANT: The replacement package must be target-compatible
zlib_ng = textwrap.dedent("""
dep = textwrap.dedent(f"""
from conan import ConanFile
class ZlibNG(ConanFile):
name = "zlib-ng"
version = "0.1"
name = "{name}"
version = "{version}"
def package_info(self):
self.cpp_info.set_property("cmake_file_name", "ZLIB")
self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB")
Expand All @@ -189,24 +193,78 @@ def package_info(self):
self.output.info(f"DEP ZLIB package_info: {self.dependencies['zlib'].ref.name}!")
self.cpp_info.requires = ["zlib::zlib"]
""")
c.save({"zlibng/conanfile.py": zlib_ng,
c.save({"dep/conanfile.py": dep,
"app/conanfile.py": conanfile,
"profile": "[replace_requires]\nzlib/0.1: zlib-ng/0.1"})
c.run("create zlibng")
"profile": f"[replace_requires]\nzlib/0.1: {name}/{version}"})
c.run("create dep")
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
assert f"zlib/0.1: {name}/{version}" in c.out
assert f"conanfile.py (app/0.1): DEP ZLIB generate: {name}!" in c.out
assert f"conanfile.py (app/0.1): DEP ZLIB build: {name}!" 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
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 f"zlib/0.1: {name}/{version}" in c.out
assert f"app/0.1: DEP ZLIB generate: {name}!" in c.out
assert f"app/0.1: DEP ZLIB build: {name}!" in c.out


def test_replace_requires_consumer_references_error_multiple():
# https://github.com/conan-io/conan/issues/17407
c = TestClient()
# IMPORTANT: The replacement package must be target-compatible
zlib = textwrap.dedent("""
from conan import ConanFile
class ZlibNG(ConanFile):
name = "zlib"
version = "0.2"
def package_info(self):
self.cpp_info.set_property("cmake_file_name", "ZLIB")
self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB")
""")
conanfile = textwrap.dedent("""
from conan import ConanFile
class App(ConanFile):
name = "app"
version = "0.1"
settings = "build_type"
requires = "zlib/0.1", "bzip2/0.1"
generators = "CMakeDeps"
def generate(self):
self.output.info(f"DEP ZLIB generate: {self.dependencies['zlib'].ref.name}!")
self.output.info(f"DEP BZIP2 generate: {self.dependencies['bzip2'].ref.name}!")
def build(self):
self.output.info(f"DEP ZLIB build: {self.dependencies['zlib'].ref.name}!")
self.output.info(f"DEP BZIP2 build: {self.dependencies['bzip2'].ref.name}!")
def package_info(self):
self.output.info(f"DEP ZLIB package_info: {self.dependencies['zlib'].ref.name}!")
self.cpp_info.requires = ["zlib::zlib", "bzip2::bzip2"]
""")
c.save({"zlib/conanfile.py": zlib,
"app/conanfile.py": conanfile,
"profile": "[replace_requires]\nzlib/0.1: zlib/0.2\nbzip2/0.1: zlib/0.2"})
c.run("create zlib")
c.run("build app -pr=profile")
assert "zlib/0.1: zlib/0.2" in c.out
assert "conanfile.py (app/0.1): DEP ZLIB generate: zlib!" in c.out
assert "conanfile.py (app/0.1): DEP ZLIB build: zlib!" in c.out
assert "conanfile.py (app/0.1): DEP BZIP2 generate: zlib!" in c.out
assert "conanfile.py (app/0.1): DEP BZIP2 build: zlib!" 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 ZLIB::ZLIB)" in c.out
cmake = c.load("app/ZLIBTargets.cmake")
assert "add_library(ZLIB::ZLIB INTERFACE IMPORTED)" in cmake
c.run("create app -pr=profile")
assert "zlib/0.1: zlib/0.2" in c.out
assert "app/0.1: DEP ZLIB generate: zlib!" in c.out
assert "app/0.1: DEP ZLIB build: zlib!" in c.out


def test_replace_requires_consumer_components_options():
Expand Down

0 comments on commit 26caf37

Please sign in to comment.