diff --git a/conans/model/requires.py b/conans/model/requires.py index 23987d0dc81..b1974a7dc6f 100644 --- a/conans/model/requires.py +++ b/conans/model/requires.py @@ -270,15 +270,18 @@ def transform_downstream(self, pkg_type, require, dep_pkg_type): if require.build: # public! # TODO: To discuss if this way of conflicting build_requires is actually useful or not + # Build-requires will propagate its main trait for running exes/shared to downstream + # consumers so run=require.run, irrespective of the 'self.run' trait downstream_require = Requirement(require.ref, headers=False, libs=False, build=True, - run=False, visible=self.visible, direct=False) + run=require.run, visible=self.visible, direct=False) return downstream_require if self.build: # Build-requires # If the above is shared or the requirement is explicit run=True + # visible=self.visible will further propagate it downstream if dep_pkg_type is PackageType.SHARED or require.run: downstream_require = Requirement(require.ref, headers=False, libs=False, build=True, - run=True, visible=False, direct=False) + run=True, visible=self.visible, direct=False) return downstream_require return diff --git a/test/integration/graph/core/test_build_requires.py b/test/integration/graph/core/test_build_requires.py index acf8c40e23b..7e396044b72 100644 --- a/test/integration/graph/core/test_build_requires.py +++ b/test/integration/graph/core/test_build_requires.py @@ -639,7 +639,89 @@ def test_simple(self): # node, include, link, build, run _check_transitive(lib, [(cmake, False, False, True, True)]) _check_transitive(app, [(lib, True, True, False, False), - (cmake, False, False, True, False)]) + (cmake, False, False, True, True)]) + + def test_deep_dependency_tree(self): + # app -> liba -> libb-(br public) -> sfun -> libsfun -> libx -> liby -> libz + # -(normal req) -> libsfun -> libx -> liby -> libz + self.recipe_conanfile("libz/0.1", GenConanfile()) + self.recipe_conanfile("liby/0.1", GenConanfile().with_requirement("libz/0.1", run=True)) + self.recipe_conanfile("libx/0.1", GenConanfile().with_requirement("liby/0.1", run=True)) + self.recipe_conanfile("libsfun/0.1", GenConanfile().with_requirement("libx/0.1", run=True)) + self.recipe_conanfile("sfun/0.1", GenConanfile().with_requirement("libsfun/0.1", run=True)) + self.recipe_conanfile("libb/0.1", GenConanfile() + .with_tool_requirement("sfun/0.1", visible=True) + .with_requirement("libsfun/0.1", run=True)) + self.recipe_conanfile("liba/0.1", GenConanfile().with_requirement("libb/0.1", run=True)) + deps_graph = self.build_graph(GenConanfile("app", "0.1").with_requirement("liba/0.1", run=True)) + + # Build requires always apply to the consumer + self.assertEqual(8 + 4, len(deps_graph.nodes)) + app = deps_graph.root + liba = app.dependencies[0].dst + libb = liba.dependencies[0].dst + libsfun = libb.dependencies[0].dst + libx = libsfun.dependencies[0].dst + liby = libx.dependencies[0].dst + libz = liby.dependencies[0].dst + sfun = libb.dependencies[1].dst + libsfun_build = sfun.dependencies[0].dst + libx_build = libsfun_build.dependencies[0].dst + liby_build = libx_build.dependencies[0].dst + libz_build = liby_build.dependencies[0].dst + + # TODO non-build-requires + + self._check_node(app, "app/0.1@", deps=[liba], dependents=[]) + self._check_node(liba, "liba/0.1#123", deps=[libb], dependents=[app]) + self._check_node(libb, "libb/0.1#123", deps=[sfun, libsfun], dependents=[liba]) + self._check_node(sfun, "sfun/0.1#123", deps=[libsfun_build], dependents=[libb]) + self._check_node(libsfun_build, "libsfun/0.1#123", deps=[libx_build], dependents=[sfun]) + self._check_node(libx_build, "libx/0.1#123", deps=[liby_build], dependents=[libsfun_build]) + self._check_node(liby_build, "liby/0.1#123", deps=[libz_build], dependents=[libx_build]) + self._check_node(libz_build, "libz/0.1#123", deps=[], dependents=[liby_build]) + + # node, include, link, build, run + _check_transitive(liby_build, [(libz_build, True, True, False, True)]) + _check_transitive(libx_build, [(liby_build, True, True, False, True), + (libz_build, True, True, False, True)]) + _check_transitive(libsfun_build, [(libx_build, True, True, False, True), + (liby_build, True, True, False, True), + (libz_build, True, True, False, True)]) + _check_transitive(sfun, [(libsfun_build, True, True, False, True), + (libx_build, True, True, False, True), + (liby_build, True, True, False, True), + (libz_build, True, True, False, True)]) + _check_transitive(libb, [(libsfun, True, True, False, True), + (libx, True, True, False, True), + (liby, True, True, False, True), + (libz, True, True, False, True), + (sfun, False, False, True, True), + (libsfun_build, False, False, True, True), + (libx_build, False, False, True, True), + (liby_build, False, False, True, True), + (libz_build, False, False, True, True)]) + _check_transitive(liba, [(libb, True, True, False, True), + (libsfun, True, True, False, True), + (libx, True, True, False, True), + (liby, True, True, False, True), + (libz, True, True, False, True), + (sfun, False, False, True, True), + (libsfun_build, False, False, True, True), + (libx_build, False, False, True, True), + (liby_build, False, False, True, True), + (libz_build, False, False, True, True)]) + _check_transitive(app, [(liba, True, True, False, True), + (libb, True, True, False, True), + (libsfun, True, True, False, True), + (libx, True, True, False, True), + (liby, True, True, False, True), + (libz, True, True, False, True), + (sfun, False, False, True, True), + (libsfun_build, False, False, True, True), + (libx_build, False, False, True, True), + (liby_build, False, False, True, True), + (libz_build, False, False, True, True)]) def test_conflict_diamond(self): # app -> libb -(br public)-> cmake/0.1 @@ -668,6 +750,40 @@ def test_conflict_diamond(self): self._check_node(libb, "libb/0.1#123", deps=[cmake1], dependents=[app]) self._check_node(cmake1, "cmake/0.1#123", deps=[], dependents=[libb]) + def test_conflict_diamond_two_levels(self): + # app -> libd -> libb -(br public)-> cmake/0.1 + # \--> libe -> libc -(br public)-> cmake/0.2 + self.recipe_conanfile("cmake/0.1", GenConanfile()) + self.recipe_conanfile("cmake/0.2", GenConanfile()) + self.recipe_conanfile("libb/0.1", + GenConanfile().with_tool_requirement("cmake/0.1", visible=True)) + self.recipe_conanfile("libc/0.1", + GenConanfile().with_tool_requirement("cmake/0.2", visible=True)) + self.recipe_conanfile("libd/0.1", GenConanfile().with_requires("libb/0.1")) + self.recipe_conanfile("libe/0.1", GenConanfile().with_requires("libc/0.1")) + + deps_graph = self.build_graph(GenConanfile("app", "0.1").with_requires("libd/0.1", + "libe/0.1"), + install=False) + + assert type(deps_graph.error) == GraphConflictError + + # Build requires always apply to the consumer + self.assertEqual(6, len(deps_graph.nodes)) + app = deps_graph.root + libd = app.dependencies[0].dst + libe = app.dependencies[1].dst + libb = libd.dependencies[0].dst + libc = libe.dependencies[0].dst + cmake1 = libb.dependencies[0].dst + + self._check_node(app, "app/0.1@", deps=[libd, libe], dependents=[]) + self._check_node(libd, "libd/0.1#123", deps=[libb], dependents=[app]) + self._check_node(libe, "libe/0.1#123", deps=[libc], dependents=[app]) + self._check_node(libb, "libb/0.1#123", deps=[cmake1], dependents=[libd]) + self._check_node(libc, "libc/0.1#123", deps=[], dependents=[libe]) + self._check_node(cmake1, "cmake/0.1#123", deps=[], dependents=[libb]) + def test_tool_requires(self): # app -> libb -(br public)-> protobuf/0.1 # \--------------> protobuf/0.2 @@ -695,7 +811,7 @@ def test_tool_requires(self): # node, headers, lib, build, run _check_transitive(app, [(libb, True, True, False, False), (protobuf_host, True, True, False, False), - (protobuf_build, False, False, True, False)]) + (protobuf_build, False, False, True, True)]) def test_tool_requires_override(self): # app -> libb -(br public)-> protobuf/0.1 @@ -726,7 +842,7 @@ def test_tool_requires_override(self): # node, headers, lib, build, run _check_transitive(app, [(libb, True, True, False, False), (protobuf_host, True, True, False, False), - (protobuf_build, False, False, True, False)]) + (protobuf_build, False, False, True, True)]) _check_transitive(libb, [(protobuf_host, True, True, False, False), (protobuf_build, False, False, True, True)])