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 <host_version:track ref> to Conan v1 #15287

Merged
Merged
Show file tree
Hide file tree
Changes from 2 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
21 changes: 16 additions & 5 deletions conans/client/graph/graph_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,18 +404,29 @@ 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>":
ref_version = str(ref.version)
if ref_version.startswith("<host_version") and ref_version.endswith(">"):
if not requirement.build_require:
raise ConanException(f"{current_node.ref} uses '<host_version>' in requires, "
"it is only allowed in tool_requires")
search_ref_name = ref.name
tracking_ref = ref_version.split(":", 1)
if len(tracking_ref) == 2:
search_ref_name = tracking_ref[1][:-1] # Remove trailing '>'
transitive = next((dep for dep in current_node.dependencies if
not dep.build_require and dep.require.ref.name == ref.name), None)
not dep.build_require and
dep.require.ref.name == search_ref_name and
dep.require.ref.user == ref.user and
dep.require.ref.channel == ref.channel), None)
if transitive is None:
raise ConanException(
f"{current_node.ref} require '{ref}': didn't find a matching host dependency")
requirement.ref = transitive.require.ref
f"{current_node.ref} require '{search_ref_name}': didn't find a matching host dependency")
new_ref = transitive.require.ref
# Final ref is the one from the transitive dependency, but with the version from the
# tracked requirement, if any
requirement.ref = ConanFileReference(ref.name, new_ref.version,
new_ref.user, new_ref.channel)

try:
result = self._proxy.get_recipe(requirement.ref, check_updates, update,
Expand Down
3 changes: 2 additions & 1 deletion conans/model/ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ def validate_name(name, reference_token=None):
@staticmethod
def validate_version(version, pkg_name):
ConanName.validate_string(version)
if version == "*" or version == "<host_version>":
version_str = str(version)
if version == "*" or (version_str.startswith("<host_version") and version_str.endswith(">")):
return
if ConanName._validation_pattern.match(version) is None:
if (
Expand Down
54 changes: 53 additions & 1 deletion conans/test/integration/build_requires/build_requires_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ def test_track_host_error_nothost(self, build_profile):
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
assert "pkg/None require 'protobuf': didn't find a matching host dependency" in c.out
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved

@pytest.mark.parametrize("build_profile", [False, True])
def test_track_host_error_wrong_context(self, build_profile):
Expand Down Expand Up @@ -670,3 +670,55 @@ def test(self):
# This used to fail
c.run(f"create pkg {build_profile}")
assert "protobuf/1.0 from local cache - Cache" in c.out

@pytest.mark.parametrize("host_version, assert_error, assert_msg", [
("libgettext>", False, "gettext/0.2:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Cache"),
# Error cases, just checking that we fail gracefully - no tracebacks
("libgettext", True, "Package gettext has an invalid version number: '<host_version:libgettext'"),
(":>", True, "app/1.0 require ':': didn't find a matching host dependency"),
(">", True, "app/1.0 require '': didn't find a matching host dependency"),
(":", True, " Package gettext has an invalid version number: '<host_version::'"),
("", True, " Package gettext has an invalid version number: '<host_version:'")
])
def test_host_version_different_ref(self, host_version, assert_error, assert_msg):
tc = TestClient()
tc.save({"gettext/conanfile.py": GenConanfile("gettext"),
"libgettext/conanfile.py": GenConanfile("libgettext"),
"app/conanfile.py": GenConanfile("app", "1.0").with_requires("libgettext/[>0.1]")
.with_build_requires(f"gettext/<host_version:{host_version}")})
tc.run("create libgettext libgettext/0.2@")
tc.run("create gettext gettext/0.1@ --build-require")
tc.run("create gettext gettext/0.2@ --build-require")

tc.run("create app app/1.0@", assert_error=assert_error)
assert assert_msg in tc.out

@pytest.mark.parametrize("requires_tag,tool_requires_tag,fails", [
("user/channel", "user/channel", False),
("", "user/channel", True),
("auser/achannel", "anotheruser/anotherchannel", True),
])
def test_overriden_host_version_user_channel(self, requires_tag, tool_requires_tag, fails):
"""
Make the tool_requires follow the regular require with the expression "<host_version>"
"""
c = TestClient()
pkg = textwrap.dedent(f"""
from conan import ConanFile
class ProtoBuf(ConanFile):
name = "pkg"
version = "0.1"
def requirements(self):
self.requires("protobuf/1.0@{requires_tag}")
def build_requirements(self):
self.tool_requires("protobuf/<host_version>@{tool_requires_tag}")
""")
c.save({"protobuf/conanfile.py": GenConanfile("protobuf"),
"pkg/conanfile.py": pkg})
c.run(f"create protobuf 1.0@{requires_tag}")

c.run("create pkg", assert_error=fails)
if fails:
assert "pkg/0.1 require 'protobuf': didn't find a matching host dependency" in c.out
else:
assert "Package '08f50edefe03c3273e386557efc23dc41a9f600b' created" in c.out