diff --git a/conans/client/graph/graph.py b/conans/client/graph/graph.py index 29c1094d3f0..2634ceea6e7 100644 --- a/conans/client/graph/graph.py +++ b/conans/client/graph/graph.py @@ -94,6 +94,7 @@ def propagate_downstream(self, require, node, src_node=None): return True require.aggregate(existing.require) + assert not require.version_range # No ranges slip into transitive_deps definitions # TODO: Might need to move to an update() for performance self.transitive_deps.pop(require, None) self.transitive_deps[require] = TransitiveRequirement(require, node) diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index f6940718cb1..695e962f81b 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -219,7 +219,7 @@ def _resolve_recipe(self, ref, graph_lock): @staticmethod def _resolved_system_tool(node, require, profile_build, profile_host, resolve_prereleases): - if node.context == CONTEXT_HOST and not require.build: # Only for tool_requires + if node.context == CONTEXT_HOST and not require.build: # Only for DIRECT tool_requires return system_tool = profile_build.system_tools if node.context == CONTEXT_BUILD \ else profile_host.system_tools @@ -229,9 +229,13 @@ def _resolved_system_tool(node, require, profile_build, profile_host, resolve_pr if require.ref.name == d.name: if version_range: if version_range.contains(d.version, resolve_prereleases): + require.ref.version = d.version # resolved range is replaced by exact return d, ConanFile(str(d)), RECIPE_SYSTEM_TOOL, None elif require.ref.version == d.version: - return d, ConanFile(str(d)), RECIPE_SYSTEM_TOOL, None + if d.revision is None or require.ref.revision is None or \ + d.revision == require.ref.revision: + require.ref.revision = d.revision + return d, ConanFile(str(d)), RECIPE_SYSTEM_TOOL, None def _create_new_node(self, node, require, graph, profile_host, profile_build, graph_lock): resolved = self._resolved_system_tool(node, require, profile_build, profile_host, diff --git a/conans/test/integration/graph/test_system_tools.py b/conans/test/integration/graph/test_system_tools.py index 7bb7023324b..0e5b2799742 100644 --- a/conans/test/integration/graph/test_system_tools.py +++ b/conans/test/integration/graph/test_system_tools.py @@ -68,6 +68,73 @@ def test_graph_info_system_tool_require_range(self): client.run("graph info . -pr=profile") assert "tool/1.1 - System tool" in client.out + def test_consumer_resolved_version(self): + client = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + tool_requires = "tool/[>=1.0]" + + def generate(self): + for r, _ in self.dependencies.items(): + self.output.info(f"DEPENDENCY {r.ref}") + """) + client.save({"conanfile.py": conanfile, + "profile": "[system_tools]\ntool/1.1"}) + client.run("install . -pr=profile") + assert "tool/1.1 - System tool" in client.out + assert "conanfile.py: DEPENDENCY tool/1.1" in client.out + + def test_consumer_resolved_revision(self): + client = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + tool_requires = "tool/1.1" + + def generate(self): + for r, _ in self.dependencies.items(): + self.output.info(f"DEPENDENCY {repr(r.ref)}") + """) + client.save({"conanfile.py": conanfile, + "profile": "[system_tools]\ntool/1.1#rev1"}) + client.run("install . -pr=profile") + assert "tool/1.1 - System tool" in client.out + assert "conanfile.py: DEPENDENCY tool/1.1#rev1" in client.out + + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + tool_requires = "tool/1.1#rev1" + + def generate(self): + for r, _ in self.dependencies.items(): + self.output.info(f"DEPENDENCY {repr(r.ref)}") + """) + client.save({"conanfile.py": conanfile}) + client.run("install . -pr=profile") + assert "tool/1.1 - System tool" in client.out + assert "conanfile.py: DEPENDENCY tool/1.1#rev1" in client.out + + def test_consumer_unresolved_revision(self): + """ if a recipe specifies an exact revision and so does the profiƱe + and it doesn't match, it is an error + """ + client = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + tool_requires = "tool/1.1#rev2" + + def generate(self): + for r, _ in self.dependencies.items(): + self.output.info(f"DEPENDENCY {repr(r.ref)}") + """) + client.save({"conanfile.py": conanfile, + "profile": "[system_tools]\ntool/1.1#rev1"}) + client.run("install . -pr=profile", assert_error=True) + assert "ERROR: Package 'tool/1.1' not resolved" in client.out + class TestToolRequiresLock: