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/transitive build requires minimal changes #16849

Merged
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
7 changes: 5 additions & 2 deletions conans/model/requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
122 changes: 119 additions & 3 deletions test/integration/graph/core/test_build_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)])

Expand Down