diff --git a/conans/model/build_info.py b/conans/model/build_info.py index a29b09bda1f..ee5478825b8 100644 --- a/conans/model/build_info.py +++ b/conans/model/build_info.py @@ -608,8 +608,15 @@ def _get_sorted_components(self): del components[comp_name] break else: + dset = set() + for comp_name, comp in components.items(): + for dep_name, dep in components.items(): + for require in self._filter_component_requires(dep.requires): + if require == comp_name: + dset.add(" {} requires {}".format(dep_name, comp_name)) + dep_mesg = "\n".join(dset) raise ConanException("There is a dependency loop in " - "'self.cpp_info.components' requires") + "'self.cpp_info.components' requires:\n{}".format(dep_mesg)) self._sorted_components = ordered else: # If components do not have requirements, keep them in the same order self._sorted_components = self._cpp_info.components diff --git a/conans/test/integration/test_components.py b/conans/test/integration/test_components.py new file mode 100644 index 00000000000..202dae8d338 --- /dev/null +++ b/conans/test/integration/test_components.py @@ -0,0 +1,25 @@ +import textwrap + +from conans.test.utils.tools import TestClient + + +def test_components_cycles(): + c = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + + class TestcycleConan(ConanFile): + name = "testcycle" + version = "1.0" + + def package_info(self): + self.cpp_info.components["c"].requires = ["b"] + self.cpp_info.components["b"].requires = ["a"] + self.cpp_info.components["a"].requires = ["c"] # cycle! + """) + c.save({"conanfile.py": conanfile}) + c.run("create .", assert_error=True) + assert "ERROR: There is a dependency loop in 'self.cpp_info.components' requires:" in c.out + assert "a requires c" + assert "b requires a" + assert "c rquires b"