Skip to content

Commit 00dbefc

Browse files
authored
Merge pull request #63 from rabbitmq/improve-dep-resolution-error
Improve the error message when erlang_package deps conflict
2 parents 29ef2cb + 9795570 commit 00dbefc

File tree

4 files changed

+98
-59
lines changed

4 files changed

+98
-59
lines changed

bzlmod/erlang_package.bzl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ load(
1313
)
1414

1515
HexPackage = provider(fields = [
16+
"module",
1617
"name",
1718
"version",
1819
"sha256",
@@ -24,6 +25,7 @@ HexPackage = provider(fields = [
2425
])
2526

2627
GitPackage = provider(fields = [
28+
"module",
2729
"name",
2830
"version",
2931
"remote",
@@ -41,7 +43,7 @@ def log(ctx, msg):
4143

4244
def hex_tree(
4345
ctx,
44-
otp_installation_names = None,
46+
module = None,
4547
name = None,
4648
version = None):
4749
log(ctx, "Fetching release info for {}@{} from hex.pm".format(name, version))
@@ -57,6 +59,7 @@ def hex_tree(
5759
requirements.append(props)
5860

5961
return HexPackage(
62+
module = module,
6063
name = name,
6164
version = version,
6265
sha256 = sha256,
@@ -69,13 +72,14 @@ def hex_tree(
6972

7073
def hex_package(
7174
ctx,
72-
otp_installation_names = None,
75+
module = None,
7376
name = None,
7477
version = None,
7578
sha256 = None,
7679
build_file_content = None,
7780
patch_cmds = None):
7881
return HexPackage(
82+
module = module,
7983
name = name,
8084
version = version,
8185
sha256 = sha256,
@@ -96,7 +100,7 @@ def _infer_app_name(remote):
96100

97101
def git_package(
98102
ctx,
99-
otp_installation_names = None,
103+
module = None,
100104
dep = None):
101105
if dep.remote != "" and dep.repository != "":
102106
fail("'remote' and 'repository' are mutually exclusive options")
@@ -121,6 +125,7 @@ def git_package(
121125
version = dep.branch
122126

123127
return GitPackage(
128+
module = module,
124129
name = name,
125130
version = version,
126131
remote = remote,
@@ -145,6 +150,7 @@ def without_requirement(name, package):
145150
new_requirements.append(r)
146151

147152
return HexPackage(
153+
module = package.module,
148154
name = package.name,
149155
version = package.version,
150156
sha256 = package.sha256,

bzlmod/extensions.bzl

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
load(
22
":hex_pm.bzl",
33
"hex_package_info",
4-
"newest",
54
"satisfies",
65
)
76
load(
@@ -12,6 +11,11 @@ load(
1211
"log",
1312
"without_requirement",
1413
)
14+
load(
15+
":semver.bzl",
16+
"lt",
17+
"version_from_string",
18+
)
1519
load(
1620
"//:rules_erlang.bzl",
1721
"xref_runner_sources",
@@ -76,6 +80,25 @@ def _resolve_hex_pm(ctx, packages):
7680
return resolved
7781
fail("Dependencies were not resolved after {} passes.".format(_RESOLVE_MAX_PASSES))
7882

83+
def _newest(a, b):
84+
if a.version == b.version:
85+
return a
86+
87+
a_version = version_from_string(a.version)
88+
b_version = version_from_string(b.version)
89+
if a_version == None or b_version == None:
90+
fail("Version {dep_name}@{a_version} (required by {a_module}) & {dep_name}@{b_version} (required by {b_module}) cannot be resolved".format(
91+
dep_name = a.name,
92+
a_version = a.version,
93+
a_module = a.module.name,
94+
b_version = b.version,
95+
b_module = b.module.name,
96+
))
97+
elif lt(a_version, b_version):
98+
return b
99+
else:
100+
return a
101+
79102
def _resolve_local(packages):
80103
deduped = []
81104
packages_by_name = {}
@@ -87,7 +110,7 @@ def _resolve_local(packages):
87110
for (_, dupes) in packages_by_name.items():
88111
p = dupes[0]
89112
for dupe in dupes[1:]:
90-
p = newest(p, dupe)
113+
p = _newest(p, dupe)
91114
deduped.append(p)
92115
return deduped
93116

@@ -99,12 +122,14 @@ def _erlang_package(ctx):
99122
for dep in mod.tags.hex_package_tree:
100123
packages.append(hex_tree(
101124
ctx,
125+
module = mod,
102126
name = dep.name,
103127
version = dep.version,
104128
))
105129
for dep in mod.tags.hex_package:
106130
packages.append(hex_package(
107131
ctx,
132+
module = mod,
108133
name = dep.name,
109134
version = dep.version,
110135
sha256 = dep.sha256,
@@ -114,6 +139,7 @@ def _erlang_package(ctx):
114139
for dep in mod.tags.git_package:
115140
packages.append(git_package(
116141
ctx,
142+
module = mod,
117143
dep = dep,
118144
))
119145

bzlmod/hex_pm.bzl

Lines changed: 14 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
load(
2+
":semver.bzl",
3+
"Version",
4+
"eq",
5+
"gte",
6+
"lt",
7+
"version_from_string",
8+
)
9+
110
def hex_package_info(ctx, name):
211
curl = ctx.which("curl")
312
endpoint = "https://hex.pm/api/packages/{}".format(name)
@@ -20,68 +29,19 @@ def hex_release_info(ctx, name, version):
2029
fail("hex.pm api returned {} for {}".format(response.return_code, endpoint))
2130
return json.decode(response.stdout)
2231

23-
Version = provider(fields = ["major", "minor", "patch"])
24-
25-
def _version(version):
26-
[major, minor, patch] = version.split(".", 2)
27-
if not patch.isdigit():
28-
return None
29-
return Version(
30-
major = int(major),
31-
minor = int(minor),
32-
patch = int(patch),
33-
)
34-
35-
def _gte(a, b):
36-
if a.major > b.major:
37-
return True
38-
elif a.major == b.major:
39-
if a.minor > b.minor:
40-
return True
41-
elif a.minor == b.minor:
42-
return a.patch >= b.patch
43-
else:
44-
return False
45-
else:
46-
return False
47-
48-
def _lt(a, b):
49-
if a.major < b.major:
50-
return True
51-
elif a.major == b.major:
52-
if a.minor < b.minor:
53-
return True
54-
elif a.minor == b.minor:
55-
return a.patch < b.patch
56-
else:
57-
return False
58-
else:
59-
return False
60-
61-
def _eq(a, b):
62-
return a.major == b.major and a.minor == b.minor and a.patch == b.patch
63-
64-
def newest(a, b):
65-
if a.version == b.version:
66-
return a
67-
if _lt(_version(a.version), _version(b.version)):
68-
return b
69-
else:
70-
return a
71-
7232
def satisfies(version, requirement):
73-
v = _version(version)
33+
v = version_from_string(version)
7434
if v == None:
7535
# print("Ignoring version", version)
7636
return False
7737
if requirement.startswith("~>"):
78-
min = _version(requirement.removeprefix("~>").lstrip())
38+
min = version_from_string(requirement.removeprefix("~>").lstrip())
7939
if min == None:
8040
print("Ignoring requirement", requirement)
8141
return False
8242
max = Version(major = min.major, minor = min.minor + 1, patch = 0)
83-
return _gte(v, min) and _lt(v, max)
84-
elif _version(requirement) != None:
85-
return _eq(v, _version(requirement))
43+
return gte(v, min) and lt(v, max)
44+
elif version_from_string(requirement) != None:
45+
return eq(v, version_from_string(requirement))
8646
else:
8747
fail(requirement)

bzlmod/semver.bzl

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
Version = provider(fields = ["major", "minor", "patch"])
2+
3+
def version_from_string(version):
4+
parts = version.split(".", 2)
5+
if len(parts) != 3:
6+
return None
7+
[major, minor, patch] = parts
8+
if not major.isdigit():
9+
return None
10+
if not minor.isdigit():
11+
return None
12+
if not patch.isdigit():
13+
return None
14+
return Version(
15+
major = int(major),
16+
minor = int(minor),
17+
patch = int(patch),
18+
)
19+
20+
def gte(a, b):
21+
if a.major > b.major:
22+
return True
23+
elif a.major == b.major:
24+
if a.minor > b.minor:
25+
return True
26+
elif a.minor == b.minor:
27+
return a.patch >= b.patch
28+
else:
29+
return False
30+
else:
31+
return False
32+
33+
def lt(a, b):
34+
if a.major < b.major:
35+
return True
36+
elif a.major == b.major:
37+
if a.minor < b.minor:
38+
return True
39+
elif a.minor == b.minor:
40+
return a.patch < b.patch
41+
else:
42+
return False
43+
else:
44+
return False
45+
46+
def eq(a, b):
47+
return a.major == b.major and a.minor == b.minor and a.patch == b.patch

0 commit comments

Comments
 (0)