Skip to content

Commit f495493

Browse files
authored
#5617 Report dependants when a conflict occurs (#5684)
* #5617 Report dependants when a conflict occurs - Update test to validate all conflict message - Retrieve dependants from previous graph node Signed-off-by: Uilian Ries <uilianries@gmail.com> * #5617 Check for alias before the exception Signed-off-by: Uilian Ries <uilianries@gmail.com> * #5617 Update conflict message Signed-off-by: Uilian Ries <uilianries@gmail.com> * #5617 Fix tests for conflict message Signed-off-by: Uilian Ries <uilianries@gmail.com>
1 parent e483a74 commit f495493

File tree

7 files changed

+80
-32
lines changed

7 files changed

+80
-32
lines changed

conans/client/graph/graph_builder.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,12 @@ def _expand_require(self, require, node, graph, check_updates, update, remotes,
233233
else: # a public node already exist with this name
234234
self._resolve_cached_alias([require], graph)
235235
# As we are closing a diamond, there can be conflicts. This will raise if conflicts
236-
conflict = self._conflicting_references(previous.ref, require.ref, node.ref)
236+
conflict = self._conflicting_references(previous, require.ref, node.ref)
237237
if conflict: # It is possible to get conflict from alias, try to resolve it
238238
self._resolve_recipe(node, graph, require, check_updates,
239239
update, remotes, profile_host, graph_lock)
240240
# Maybe it was an ALIAS, so we can check conflict again
241-
conflict = self._conflicting_references(previous.ref, require.ref, node.ref)
241+
conflict = self._conflicting_references(previous, require.ref, node.ref)
242242
if conflict:
243243
raise ConanException(conflict)
244244

@@ -268,18 +268,19 @@ def _expand_require(self, require, node, graph, check_updates, update, remotes,
268268
update, remotes, profile_host, profile_build, graph_lock, context)
269269

270270
@staticmethod
271-
def _conflicting_references(previous_ref, new_ref, consumer_ref=None):
272-
if previous_ref.copy_clear_rev() != new_ref.copy_clear_rev():
271+
def _conflicting_references(previous, new_ref, consumer_ref=None):
272+
if previous.ref.copy_clear_rev() != new_ref.copy_clear_rev():
273273
if consumer_ref:
274-
return ("Conflict in %s\n"
275-
" Requirement %s conflicts with already defined %s\n"
276-
" To change it, override it in your base requirements"
277-
% (consumer_ref, new_ref, previous_ref))
274+
return ("Conflict in %s:\n"
275+
" '%s' requires '%s' while '%s' requires '%s'.\n"
276+
" To fix this conflict you need to override the package '%s' in your root package."
277+
% (consumer_ref, consumer_ref, new_ref, next(iter(previous.dependants)).src,
278+
previous.ref, new_ref.name))
278279
return True
279280
# Computed node, if is Editable, has revision=None
280281
# If new_ref.revision is None we cannot assume any conflict, the user hasn't specified
281282
# a revision, so it's ok any previous_ref
282-
if previous_ref.revision and new_ref.revision and previous_ref.revision != new_ref.revision:
283+
if previous.ref.revision and new_ref.revision and previous.ref.revision != new_ref.revision:
283284
if consumer_ref:
284285
raise ConanException("Conflict in %s\n"
285286
" Different revisions of %s has been requested"
@@ -293,7 +294,7 @@ def _recurse(self, closure, new_reqs, new_options, context):
293294
then, incompatibilities will be raised as usually"""
294295
for req in new_reqs.values():
295296
n = closure.get(req.ref.name, context=context)
296-
if n and self._conflicting_references(n.ref, req.ref):
297+
if n and self._conflicting_references(n, req.ref):
297298
return True
298299
for pkg_name, options_values in new_options.items():
299300
n = closure.get(pkg_name, context=context)

conans/test/functional/graph/alias_test.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ def test_conflicting_alias(self):
3737
consumer = self.recipe_consumer("app/0.1", ["libb/0.1"], build_requires=["liba/latest"])
3838

3939
with six.assertRaisesRegex(self, ConanException,
40-
"Requirement liba/0.2 conflicts with already defined liba/0.1"):
40+
"Conflict in app/0.1:\n"
41+
" 'app/0.1' requires 'liba/0.2' while 'libb/0.1' requires 'liba/0.1'.\n"
42+
" To fix this conflict you need to override the package 'liba' in your root package."):
4143
self.build_consumer(consumer)
4244

4345
def test_conflicting_not_alias(self):
@@ -50,5 +52,7 @@ def test_conflicting_not_alias(self):
5052
consumer = self.recipe_consumer("app/0.1", ["libb/0.1"], build_requires=["liba/0.2"])
5153

5254
with six.assertRaisesRegex(self, ConanException,
53-
"Requirement liba/0.2 conflicts with already defined liba/0.1"):
55+
"Conflict in app/0.1:\n"
56+
" 'app/0.1' requires 'liba/0.2' while 'libb/0.1' requires 'liba/0.1'.\n"
57+
" To fix this conflict you need to override the package 'liba' in your root package."):
5458
self.build_consumer(consumer)

conans/test/functional/graph/conflict_diamond_test.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ def test_conflict(self):
4040
self._export("Hello3", "0.1", ["Hello1/0.1@lasote/stable", "Hello2/0.1@lasote/stable"],
4141
export=False)
4242
self.client.run("install . --build missing", assert_error=True)
43-
self.assertIn("Conflict in Hello2/0.1@lasote/stable", self.client.out)
43+
self.assertIn("Conflict in Hello2/0.1@lasote/stable:\n"
44+
" 'Hello2/0.1@lasote/stable' requires 'Hello0/0.2@lasote/stable' "
45+
"while 'Hello1/0.1@lasote/stable' requires 'Hello0/0.1@lasote/stable'.\n"
46+
" To fix this conflict you need to override the package 'Hello0' in "
47+
"your root package.", self.client.out)
4448
self.assertNotIn("Generated conaninfo.txt", self.client.out)
4549

4650
def test_override_silent(self):
@@ -93,6 +97,3 @@ def test_override_explicit(self):
9397
self.assertEqual(hello0["reference"], "Hello0/0.1@lasote/stable")
9498
self.assertListEqual(sorted(hello0["required_by"]),
9599
sorted(["Hello2/0.1@lasote/stable", "Hello1/0.1@lasote/stable"]))
96-
97-
98-

conans/test/functional/graph/graph_manager_test.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,10 @@ def test_diamond_conflict(self):
203203

204204
consumer = self.recipe_consumer("app/0.1", ["libb/0.1", "libc/0.1"])
205205

206-
with six.assertRaisesRegex(self, ConanException, "Requirement liba/0.2 conflicts"):
206+
with six.assertRaisesRegex(self, ConanException, "Conflict in libc/0.1:\n"
207+
" 'libc/0.1' requires 'liba/0.2' while 'libb/0.1' requires 'liba/0.1'.\n"
208+
" To fix this conflict you need to override the package 'liba' in your root "
209+
"package."):
207210
self.build_consumer(consumer)
208211

209212
def test_loop(self):
@@ -344,7 +347,11 @@ def test_conflict_transitive_build_requires(self):
344347
.with_build_require(gtest_ref))
345348

346349
with six.assertRaisesRegex(self, ConanException,
347-
"Requirement zlib/0.2@user/testing conflicts"):
350+
"Conflict in gtest/0.1@user/testing:\n"
351+
" 'gtest/0.1@user/testing' requires 'zlib/0.2@user/testing' "
352+
"while 'lib/0.1@user/testing' requires 'zlib/0.1@user/testing'."
353+
"\n To fix this conflict you need to override the package "
354+
"'zlib' in your root package."):
348355
self.build_graph(GenConanfile().with_name("app").with_version("0.1")
349356
.with_require(lib_ref))
350357

@@ -593,7 +600,11 @@ def test_conflict_private(self):
593600
self._cache_recipe(libc_ref, GenConanfile().with_name("libc").with_version("0.1")
594601
.with_require(liba_ref2))
595602
with six.assertRaisesRegex(self, ConanException,
596-
"Requirement liba/0.2@user/testing conflicts"):
603+
"Conflict in libc/0.1@user/testing:\n"
604+
" 'libc/0.1@user/testing' requires 'liba/0.2@user/testing' "
605+
"while 'libb/0.1@user/testing' requires 'liba/0.1@user/testing'."
606+
"\n To fix this conflict you need to override the package "
607+
"'liba' in your root package."):
597608
self.build_graph(GenConanfile().with_name("app").with_version("0.1")
598609
.with_require(libb_ref, private=True)
599610
.with_require(libc_ref, private=True))
@@ -659,7 +670,11 @@ def test_transitive_private_conflict(self):
659670
.with_require(grass01_ref))
660671

661672
with six.assertRaisesRegex(self, ConanException,
662-
"Requirement grass/0.2@user/testing conflicts"):
673+
"Conflict in cheetah/0.1:\n"
674+
" 'cheetah/0.1' requires 'grass/0.2@user/testing' while 'gazelle/0.1@user/testing'"
675+
" requires 'grass/0.1@user/testing'.\n"
676+
" To fix this conflict you need to override the package 'grass' in your root"
677+
" package."):
663678
self.build_graph(GenConanfile().with_name("cheetah").with_version("0.1")
664679
.with_require(gazelle_ref)
665680
.with_require(grass02_ref, private=True))
@@ -678,7 +693,11 @@ def test_build_require_conflict(self):
678693
.with_require(grass01_ref))
679694

680695
with six.assertRaisesRegex(self, ConanException,
681-
"Requirement grass/0.2@user/testing conflicts"):
696+
"Conflict in cheetah/0.1:\n"
697+
" 'cheetah/0.1' requires 'grass/0.2@user/testing' while "
698+
"'gazelle/0.1@user/testing' requires 'grass/0.1@user/testing'.\n"
699+
" To fix this conflict you need to override the package "
700+
"'grass' in your root package."):
682701
self.build_graph(GenConanfile().with_name("cheetah").with_version("0.1")
683702
.with_require(gazelle_ref)
684703
.with_build_require(grass02_ref))

conans/test/functional/graph/require_override_test.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,11 @@ def test_override(self):
3232
self.client.run("export . libC/1.0@user/channel")
3333
self._save(req_method, ["libB/1.0@user/channel", "libC/1.0@user/channel"])
3434
self.client.run("info .", assert_error=True)
35-
self.assertIn("Requirement libA/2.0@user/channel conflicts with "
36-
"already defined libA/1.0@user/channel", self.client.out)
35+
self.assertIn("Conflict in libC/1.0@user/channel:\n"
36+
" 'libC/1.0@user/channel' requires 'libA/2.0@user/channel' while "
37+
"'libB/1.0@user/channel' requires 'libA/1.0@user/channel'.\n"
38+
" To fix this conflict you need to override the package 'libA' in your root"
39+
" package.", self.client.out)
3740

3841
self._save(req_method, ["libB/1.0@user/channel", "libC/1.0@user/channel",
3942
("libA/1.0@user/channel", "override")])
@@ -42,7 +45,7 @@ def test_override(self):
4245

4346
def test_public_deps(self):
4447
client = TestClient()
45-
pkg2 = textwrap.dedent("""
48+
pkg2 = textwrap.dedent("""
4649
from conans import ConanFile
4750
class Pkg(ConanFile):
4851
requires = ("pkg/0.1@user/stable", "override"),
@@ -52,7 +55,7 @@ def package_info(self):
5255
client.save({"conanfile.py": pkg2})
5356
client.run("create . pkg2/0.1@user/stable")
5457
self.assertIn("pkg2/0.1@user/stable: PUBLIC PKG2:[]", client.out)
55-
pkg3 = textwrap.dedent("""
58+
pkg3 = textwrap.dedent("""
5659
from conans import ConanFile
5760
class Pkg(ConanFile):
5861
requires = "pkg2/0.1@user/stable", ("pkg/0.1@user/stable", "override")

conans/test/functional/graph/version_ranges_diamond_test.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Boost(ConanFile):
3131
boost/[>=1.68.0]@lasote/stable
3232
""")
3333
client.save({"conanfile.txt": conanfile}, clean_first=True)
34-
client.run("install .")
34+
client.run("install .")
3535
self.assertIn("boost/*@lasote/stable versions found in 'default' remote", client.out)
3636
self.assertIn("resolved to 'boost/1.70.0@lasote/stable' in remote 'default'", client.out)
3737
self.assertNotIn("boost/1.69.0", client.out)
@@ -298,8 +298,12 @@ def no_joint_compatibility_resolved_test(self):
298298
self.client.run("remove '*' -f")
299299
self.client.run("install Project/1.0.0@lasote/stable --build missing", assert_error=True)
300300

301-
self.assertIn("Requirement ProblemRequirement/1.0.0@lasote/stable conflicts with "
302-
"already defined ProblemRequirement/1.1.0@lasote/stable", self.client.out)
301+
self.assertIn("Conflict in RequirementOne/1.2.3@lasote/stable:\n"
302+
" 'RequirementOne/1.2.3@lasote/stable' requires "
303+
"'ProblemRequirement/1.0.0@lasote/stable' while 'RequirementTwo/4.5.6@lasote/stable'"
304+
" requires 'ProblemRequirement/1.1.0@lasote/stable'.\n"
305+
" To fix this conflict you need to override the package 'ProblemRequirement' in "
306+
"your root package.", self.client.out)
303307

304308
# Change the order, now it resolves correctly
305309
self._export("Project", "1.0.0",

conans/test/unittests/model/transitive_reqs_test.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,11 @@ class ChatConan(ConanFile):
400400
self.retriever.save_recipe(say_ref2, say_content2)
401401
self.retriever.save_recipe(hello_ref, hello_content)
402402
self.retriever.save_recipe(bye_ref, bye_content2)
403-
with six.assertRaisesRegex(self, ConanException, "Conflict in Bye/0.2@user/testing"):
403+
with six.assertRaisesRegex(self, ConanException, "Conflict in Bye/0.2@user/testing:\n"
404+
" 'Bye/0.2@user/testing' requires 'Say/0.2@user/testing' "
405+
"while 'Hello/1.2@user/testing' requires 'Say/0.1@user/testing'.\n"
406+
" To fix this conflict you need to override the package 'Say'"
407+
" in your root package."):
404408
self.build_graph(chat_content)
405409

406410
def test_diamond_conflict(self):
@@ -417,7 +421,11 @@ class ChatConan(ConanFile):
417421
self.retriever.save_recipe(hello_ref, hello_content)
418422
self.retriever.save_recipe(bye_ref, bye_content2)
419423

420-
with six.assertRaisesRegex(self, ConanException, "Conflict in Bye/0.2@user/testing"):
424+
with six.assertRaisesRegex(self, ConanException, "Conflict in Bye/0.2@user/testing:\n"
425+
" 'Bye/0.2@user/testing' requires 'Say/0.2@user/testing'"
426+
" while 'Hello/1.2@user/testing' requires 'Say/0.1@user/testing'.\n"
427+
" To fix this conflict you need to override the package 'Say'"
428+
" in your root package."):
421429
self.build_graph(chat_content)
422430

423431
def test_diamond_conflict_solved(self):
@@ -1497,7 +1505,11 @@ class LibDConan(ConanFile):
14971505
libd_ref = ConanFileReference.loads("LibD/0.1@user/testing")
14981506
self.retriever.save_recipe(libd_ref, libd_content)
14991507

1500-
with six.assertRaisesRegex(self, ConanException, "Conflict in LibB/0.1@user/testing"):
1508+
with six.assertRaisesRegex(self, ConanException, "Conflict in LibB/0.1@user/testing:\n"
1509+
" 'LibB/0.1@user/testing' requires 'LibA/0.2@user/testing' "
1510+
"while 'LibB/0.1@user/testing' requires 'LibA/0.1@user/testing'.\n"
1511+
" To fix this conflict you need to override the package 'LibA' "
1512+
"in your root package."):
15011513
self.build_graph(self.consumer_content)
15021514
self.assertIn("LibB/0.1@user/testing: requirement LibA/0.1@user/testing overridden by "
15031515
"LibD/0.1@user/testing to LibA/0.2@user/testing", str(self.output))
@@ -1516,7 +1528,11 @@ class LibDConan(ConanFile):
15161528
libd_ref = ConanFileReference.loads("LibD/0.1@user/testing")
15171529
self.retriever.save_recipe(libd_ref, libd_content)
15181530

1519-
with six.assertRaisesRegex(self, ConanException, "Conflict in LibB/0.1@user/testing"):
1531+
with six.assertRaisesRegex(self, ConanException, "Conflict in LibB/0.1@user/testing:\n"
1532+
" 'LibB/0.1@user/testing' requires 'LibA/0.2@user/testing' "
1533+
"while 'LibB/0.1@user/testing' requires 'LibA/0.1@user/testing'.\n"
1534+
" To fix this conflict you need to override the package 'LibA' in "
1535+
"your root package."):
15201536
self.build_graph(self.consumer_content)
15211537
self.assertEqual(1, str(self.output).count("LibA requirements()"))
15221538
self.assertEqual(1, str(self.output).count("LibA configure()"))

0 commit comments

Comments
 (0)