Skip to content

Commit

Permalink
backport of <host_version> to 1.X (#13719)
Browse files Browse the repository at this point in the history
* backport of <host_version> to 1.X

* wip
  • Loading branch information
memsharded authored May 4, 2023
1 parent 51c9364 commit f3c0372
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 1 deletion.
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

0 comments on commit f3c0372

Please sign in to comment.