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

backport of <host_version> to 1.X #13719

Merged
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
12 changes: 12 additions & 0 deletions conans/client/graph/graph_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,18 @@ def _config_node(node, down_ref, down_options):

def _resolve_recipe(self, current_node, dep_graph, requirement, check_updates,
update, remotes, profile, graph_lock, original_ref=None):

ref = requirement.ref
if ref.version == "<host_version>":
if not requirement.build_require:
raise ConanException(f"{current_node.ref} uses '<host_version>' in requires, "
"it is only allowed in tool_requires")
transitive = current_node._public_deps.get(ref.name, context="host")
if transitive is None:
raise ConanException(
f"{current_node.ref} require '{ref}': didn't find a matching host dependency")
requirement.ref = transitive.ref

try:
result = self._proxy.get_recipe(requirement.ref, check_updates, update,
remotes, self._recorder)
Expand Down
2 changes: 1 addition & 1 deletion conans/model/ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def validate_name(name, reference_token=None):
@staticmethod
def validate_version(version, pkg_name):
ConanName.validate_string(version)
if version == "*":
if version == "*" or version == "<host_version>":
return
if ConanName._validation_pattern.match(version) is None:
if (
Expand Down
101 changes: 101 additions & 0 deletions conans/test/integration/build_requires/build_requires_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import json
import os
import textwrap
import unittest

import pytest
from parameterized.parameterized import parameterized

from conans.model.ref import ConanFileReference
Expand Down Expand Up @@ -527,3 +529,102 @@ class BuildReqConan(ConanFile):
client.save({"conanfile.txt": consumer}, clean_first=True)
client.run("install . --build=missing")
assert "Applying build-requirement: build_req/1.0@test/test" in client.out


class TestBuildTrackHost:

@pytest.mark.parametrize("build_profile", [False, True])
def test_overriden_host_version(self, build_profile):
"""
Make the tool_requires follow the regular require with the expression "<host_version>"
"""
c = TestClient()
pkg = textwrap.dedent("""
from conan import ConanFile
class ProtoBuf(ConanFile):
name = "pkg"
version = "0.1"
def requirements(self):
self.requires("protobuf/1.0")
def build_requirements(self):
self.tool_requires("protobuf/<host_version>")
""")
c.save({"protobuf/conanfile.py": GenConanfile("protobuf"),
"pkg/conanfile.py": pkg,
"app/conanfile.py": GenConanfile().with_requires("pkg/0.1")
.with_requirement("protobuf/1.1", override=True)})
c.run("create protobuf 1.0@")
c.run("create protobuf 1.1@")
build_profile = "-pr:b default" if build_profile else ""
c.run(f"create pkg {build_profile}")
c.run(f"install pkg {build_profile}") # make sure it doesn't crash
c.run(f"install app {build_profile}")
assert "protobuf/1.1" in c.out
assert "protobuf/1.0:" not in c.out

# verify locks work
c.run(f"lock create app/conanfile.py {build_profile}")
lock = c.load("conan.lock")
assert "protobuf/1.1" in lock
assert "protobuf/1.0" not in lock
# lock can be used
c.run(f"install app --lockfile=conan.lock")
assert "protobuf/1.1" in c.out
assert "protobuf/1.0:" not in c.out

@pytest.mark.parametrize("build_profile", [False, True])
def test_overriden_host_version_version_range(self, build_profile):
"""
same as above, but using version ranges instead of overrides
"""
build_profile = "-pr:b default" if build_profile else ""
c = TestClient()
c.save({"protobuf/conanfile.py": GenConanfile("protobuf"),
"pkg/conanfile.py": GenConanfile("pkg", "0.1").with_requirement("protobuf/[*]")
.with_build_requirement("protobuf/<host_version>"),
"app/conanfile.py": GenConanfile().with_requires("pkg/0.1")})
c.run("create protobuf 1.0@")
c.run(f"create pkg {build_profile}")
c.run(f"install pkg {build_profile}") # make sure it doesn't crash
c.run(f"install app {build_profile}")
assert "protobuf/1.0" in c.out
assert "protobuf/1.1" not in c.out

c.run("create protobuf 1.1@")
c.run(f"install pkg {build_profile}") # make sure it doesn't crash
c.run(f"install app {build_profile}")
assert "protobuf/1.1" in c.out
assert "protobuf/1.0" not in c.out

# verify locks work
c.run(f"lock create app/conanfile.py {build_profile}")
lock = c.load("conan.lock")
assert "protobuf/1.1" in lock
assert "protobuf/1.0" not in lock
# lock can be used
c.run("install app --lockfile=conan.lock")
assert "protobuf/1.1" in c.out
assert "protobuf/1.0:" not in c.out

@pytest.mark.parametrize("build_profile", [False, True])
def test_track_host_error_nothost(self, build_profile):
"""
if no host requirement is defined, it will be an error
"""
build_profile = "-pr:b default" if build_profile else ""
c = TestClient()
c.save({"conanfile.py":
GenConanfile("pkg").with_build_requirement("protobuf/<host_version>")})
c.run(f"install . {build_profile}", assert_error=True)
assert "'protobuf/<host_version>': didn't find a matching host dependency" in c.out

@pytest.mark.parametrize("build_profile", [False, True])
def test_track_host_error_wrong_context(self, build_profile):
"""
it can only be used by tool_requires, not regular requires
"""
build_profile = "-pr:b default" if build_profile else ""
c = TestClient()
c.save({"conanfile.py": GenConanfile("pkg").with_requirement("protobuf/<host_version>")})
c.run(f"install . {build_profile}", assert_error=True)
assert "uses '<host_version>' in requires" in c.out