Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix incubating CMakeDeps replace_requires #17566

Draft
wants to merge 4 commits into
base: develop2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion conan/tools/cmake/cmakedeps2/target_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ def _requires(self, info, components):
# It must be the interface pkgname::pkgname target
assert required_pkg == required_comp
comp = None
default_target = f"{dep.ref.name}::{dep.ref.name}" # replace_requires
else:
comp = required_comp
default_target = f"{required_pkg}::{required_comp}"
dep_target = self._cmakedeps.get_property("cmake_target_name", dep, comp)
dep_target = dep_target or f"{required_pkg}::{required_comp}"
dep_target = dep_target or default_target
result.append(dep_target)
return result

Expand Down
9 changes: 5 additions & 4 deletions conans/model/build_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def type(self):

@type.setter
def type(self, value):
self._type = value
self._type = PackageType(value) if value is not None else None

@property
def location(self):
Expand Down Expand Up @@ -599,7 +599,8 @@ def deduce_locations(self, conanfile, component_name=""):
if self._type is None or self._type is PackageType.HEADER:
raise ConanException("Incorrect cpp_info defining location without type or header")
return
if self._type not in [None, PackageType.SHARED, PackageType.STATIC, PackageType.APP]:
if self._type is not None and self._type not in [PackageType.SHARED, PackageType.STATIC,
PackageType.APP]:
return
num_libs = len(self.libs)
if num_libs == 0:
Expand Down Expand Up @@ -789,7 +790,7 @@ def deduce_full_cpp_info(self, conanfile):
assert not self.components, f"{conanfile} cpp_info shouldn't have .libs and .components"
for lib in self.libs:
c = _Component() # Do not do a full clone, we don't need the properties
c.type = self.type # This should be a string
c.type = self.type
c.includedirs = self.includedirs
c.libdirs = self.libdirs
c.bindirs = self.bindirs
Expand All @@ -798,7 +799,7 @@ def deduce_full_cpp_info(self, conanfile):

common = self._package.clone()
common.libs = []
common.type = str(PackageType.HEADER) # the type of components is a string!
common.type = PackageType.HEADER # the type of components is a string!
common.requires = list(result.components.keys()) + (self.requires or [])
result.components["_common"] = common
else:
Expand Down
8 changes: 5 additions & 3 deletions conans/model/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def transitive_requires(self, other):
for k, v in self._data.items():
for otherk, otherv in other._data.items():
if v == otherv:
data[k] = v
data[otherk] = v # Use otherk to respect original replace_requires
return ConanFileDependencies(data)

@property
Expand Down Expand Up @@ -182,6 +182,8 @@ def get_transitive_requires(consumer, dependency):
# The build dependencies cannot be transitive in generators like CMakeDeps,
# even if users make them visible
pkg_deps = dependency.dependencies.filter({"direct": True, "build": False})
result = consumer.dependencies.transitive_requires(pkg_deps)
result = result.filter({"skip": False})
# First we filter the skipped dependencies
result = consumer.dependencies.filter({"skip": False})
# and we keep those that are really dependencies of the current package
result = result.transitive_requires(pkg_deps)
return result
283 changes: 283 additions & 0 deletions test/integration/graph/test_replace_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,286 @@ def package_info(self):
# There are actually 2 dependencies, pointing to the same node
assert "libepoxy/0.1: DEP: opengl: libgl" in c.out
assert "libepoxy/0.1: DEP: egl: libgl" in c.out


class TestReplaceRequiresTransitiveGenerators:
""" Generators are incorrectly managing replace_requires
# https://github.com/conan-io/conan/issues/17557
"""

@pytest.mark.parametrize("diamond", [True, False])
def test_no_components(self, diamond):
c = TestClient()
zlib_ng = textwrap.dedent("""
from conan import ConanFile
class ZlibNG(ConanFile):
name = "zlib-ng"
version = "0.1"
package_type = "static-library"
def package_info(self):
self.cpp_info.libs = ["zlib"]
self.cpp_info.type = "static-library"
self.cpp_info.location = "lib/zlib.lib"
self.cpp_info.set_property("cmake_file_name", "ZLIB")
self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB")
self.cpp_info.set_property("pkg_config_name", "ZLIB")
""")
openssl = textwrap.dedent("""
from conan import ConanFile
class openssl(ConanFile):
name = "openssl"
version = "0.1"
package_type = "static-library"
requires = "zlib/0.1"
def package_info(self):
self.cpp_info.libs = ["crypto"]
self.cpp_info.type = "static-library"
self.cpp_info.location = "lib/crypto.lib"
self.cpp_info.requires = ["zlib::zlib"]
""")
zlib = '"zlib/0.1"' if diamond else ""
conanfile = textwrap.dedent(f"""
from conan import ConanFile
class App(ConanFile):
name = "app"
version = "0.1"
settings = "build_type"
requires = "openssl/0.1", {zlib}
package_type = "application"
generators = "CMakeDeps", "PkgConfigDeps"
""")
profile = textwrap.dedent("""
[settings]
build_type = Release

[replace_requires]
zlib/0.1: zlib-ng/0.1
""")
c.save({"zlibng/conanfile.py": zlib_ng,
"openssl/conanfile.py": openssl,
"app/conanfile.py": conanfile,
"profile": profile})

c.run("create zlibng")
c.run("create openssl -pr=profile")
c.run("install app -pr=profile -c tools.cmake.cmakedeps:new=will_break_next")
assert "zlib/0.1: zlib-ng/0.1" in c.out

cmake = c.load("app/ZLIB-Targets-release.cmake")
assert "add_library(ZLIB::ZLIB STATIC IMPORTED)" in cmake

cmake = c.load("app/openssl-Targets-release.cmake")
assert "find_dependency(ZLIB REQUIRED CONFIG)" in cmake
assert "add_library(openssl::openssl STATIC IMPORTED)" in cmake
assert "target_link_libraries(openssl::openssl INTERFACE ZLIB::ZLIB)" in cmake

pkg_config = c.load("app/ZLIB.pc")
print(pkg_config)
# TODO: Check here the contents of both ZLIB.pc and openssl.pc

@pytest.mark.parametrize("diamond", [True, False])
def test_openssl_components(self, diamond):
c = TestClient()
zlib_ng = textwrap.dedent("""
from conan import ConanFile
class ZlibNG(ConanFile):
name = "zlib-ng"
version = "0.1"
package_type = "static-library"
def package_info(self):
self.cpp_info.libs = ["zlib"]
self.cpp_info.type = "static-library"
self.cpp_info.location = "lib/zlib.lib"
self.cpp_info.set_property("cmake_file_name", "ZLIB")
self.cpp_info.set_property("cmake_target_name", "ZLIB::ZLIB")
""")
openssl = textwrap.dedent("""
from conan import ConanFile
class openssl(ConanFile):
name = "openssl"
version = "0.1"
package_type = "static-library"
requires = "zlib/0.1"
def package_info(self):
self.cpp_info.components["crypto"].libs = ["crypto"]
self.cpp_info.components["crypto"].type = "static-library"
self.cpp_info.components["crypto"].location = "lib/crypto.lib"
self.cpp_info.components["crypto"].requires = ["zlib::zlib"]
""")
zlib = '"zlib/0.1"' if diamond else ""
conanfile = textwrap.dedent(f"""
from conan import ConanFile
class App(ConanFile):
name = "app"
version = "0.1"
settings = "build_type"
requires = "openssl/0.1", {zlib}
package_type = "application"
generators = "CMakeDeps"
""")
profile = textwrap.dedent("""
[settings]
build_type = Release

[replace_requires]
zlib/0.1: zlib-ng/0.1
""")
c.save({"zlibng/conanfile.py": zlib_ng,
"openssl/conanfile.py": openssl,
"app/conanfile.py": conanfile,
"profile": profile})

c.run("create zlibng")
c.run("create openssl -pr=profile")
c.run("install app -pr=profile -c tools.cmake.cmakedeps:new=will_break_next")
assert "zlib/0.1: zlib-ng/0.1" in c.out

cmake = c.load("app/ZLIB-Targets-release.cmake")
assert "add_library(ZLIB::ZLIB STATIC IMPORTED)" in cmake

cmake = c.load("app/openssl-Targets-release.cmake")
assert "find_dependency(ZLIB REQUIRED CONFIG)" in cmake
assert "add_library(openssl::crypto STATIC IMPORTED)" in cmake
assert "target_link_libraries(openssl::crypto INTERFACE ZLIB::ZLIB)" in cmake

@pytest.mark.parametrize("diamond", [True, False])
@pytest.mark.parametrize("explicit_requires", [True, False])
def test_zlib_components(self, diamond, explicit_requires):
c = TestClient()
zlib_ng = textwrap.dedent("""
from conan import ConanFile
class ZlibNG(ConanFile):
name = "zlib-ng"
version = "0.1"
package_type = "static-library"
def package_info(self):
self.cpp_info.components["myzlib"].libs = ["zlib"]
self.cpp_info.components["myzlib"].type = "static-library"
self.cpp_info.components["myzlib"].location = "lib/zlib.lib"
self.cpp_info.set_property("cmake_file_name", "ZLIB")
self.cpp_info.components["myzlib"].set_property("cmake_target_name",
"ZLIB::ZLIB")
""")
openssl = textwrap.dedent(f"""
from conan import ConanFile
class openssl(ConanFile):
name = "openssl"
version = "0.1"
package_type = "static-library"
requires = "zlib/0.1"
def package_info(self):
self.cpp_info.libs = ["crypto"]
self.cpp_info.type = "static-library"
self.cpp_info.location = "lib/crypto.lib"
if {explicit_requires}:
self.cpp_info.requires = ["zlib::zlib"]
""")
zlib = '"zlib/0.1"' if diamond else ""
conanfile = textwrap.dedent(f"""
from conan import ConanFile
class App(ConanFile):
name = "app"
version = "0.1"
settings = "build_type"
requires = "openssl/0.1", {zlib}
package_type = "application"
generators = "CMakeDeps"
""")
profile = textwrap.dedent("""
[settings]
build_type = Release

[replace_requires]
zlib/0.1: zlib-ng/0.1
""")
c.save({"zlibng/conanfile.py": zlib_ng,
"openssl/conanfile.py": openssl,
"app/conanfile.py": conanfile,
"profile": profile})

c.run("create zlibng")
c.run("create openssl -pr=profile")
c.run("install app -pr=profile -c tools.cmake.cmakedeps:new=will_break_next")
assert "zlib/0.1: zlib-ng/0.1" in c.out

cmake = c.load("app/ZLIB-Targets-release.cmake")
assert "add_library(ZLIB::ZLIB STATIC IMPORTED)" in cmake

cmake = c.load("app/openssl-Targets-release.cmake")
assert "find_dependency(ZLIB REQUIRED CONFIG)" in cmake
assert "add_library(openssl::openssl STATIC IMPORTED)" in cmake
# It should access the generic zlib-ng target
assert "target_link_libraries(openssl::openssl INTERFACE zlib-ng::zlib-ng)" in cmake

@pytest.mark.parametrize("diamond", [True, False])
@pytest.mark.parametrize("package_requires", [False, True])
def test_both_components(self, diamond, package_requires):
c = TestClient()
zlib_ng = textwrap.dedent("""
from conan import ConanFile
class ZlibNG(ConanFile):
name = "zlib-ng"
version = "0.1"
package_type = "static-library"
def package_info(self):
self.cpp_info.components["myzlib"].libs = ["zlib"]
self.cpp_info.components["myzlib"].type = "static-library"
self.cpp_info.components["myzlib"].location = "lib/zlib.lib"
self.cpp_info.set_property("cmake_file_name", "ZLIB")
self.cpp_info.components["myzlib"].set_property("cmake_target_name",
"ZLIB::ZLIB")
""")
openssl = textwrap.dedent(f"""
from conan import ConanFile
class openssl(ConanFile):
name = "openssl"
version = "0.1"
package_type = "static-library"
requires = "zlib/0.1"
def package_info(self):
self.cpp_info.components["crypto"].libs = ["crypto"]
self.cpp_info.components["crypto"].type = "static-library"
self.cpp_info.components["crypto"].location = "lib/crypto.lib"
if {package_requires}:
self.cpp_info.components["crypto"].requires = ["zlib::zlib"]
else:
self.cpp_info.components["crypto"].requires = ["zlib::myzlib"]
""")
zlib = '"zlib/0.1"' if diamond else ""
conanfile = textwrap.dedent(f"""
from conan import ConanFile
class App(ConanFile):
name = "app"
version = "0.1"
settings = "build_type"
requires = "openssl/0.1", {zlib}
package_type = "application"
generators = "CMakeDeps"
""")
profile = textwrap.dedent("""
[settings]
build_type = Release

[replace_requires]
zlib/0.1: zlib-ng/0.1
""")
c.save({"zlibng/conanfile.py": zlib_ng,
"openssl/conanfile.py": openssl,
"app/conanfile.py": conanfile,
"profile": profile})

c.run("create zlibng")
c.run("create openssl -pr=profile")
c.run("install app -pr=profile -c tools.cmake.cmakedeps:new=will_break_next")
assert "zlib/0.1: zlib-ng/0.1" in c.out

cmake = c.load("app/ZLIB-Targets-release.cmake")
assert "add_library(ZLIB::ZLIB STATIC IMPORTED)" in cmake

cmake = c.load("app/openssl-Targets-release.cmake")
assert "find_dependency(ZLIB REQUIRED CONFIG)" in cmake
assert "add_library(openssl::crypto STATIC IMPORTED)" in cmake
if package_requires:
assert "target_link_libraries(openssl::crypto INTERFACE zlib-ng::zlib-ng)" in cmake
else:
assert "target_link_libraries(openssl::crypto INTERFACE ZLIB::ZLIB)" in cmake
Loading