Skip to content

Commit ce67f1f

Browse files
committed
Update (redo) after sync.
1 parent cfa8a56 commit ce67f1f

File tree

4 files changed

+243
-111
lines changed

4 files changed

+243
-111
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,20 @@ See [bazel
123123
tutorial](https://docs.bazel.build/versions/main/tutorial/cc-toolchain-config.html)
124124
for how CC toolchains work in general.
125125

126+
### Requirements
127+
128+
Version attributes can be requirements of the form `latest:<condition>` or `first:<condition>`.
129+
130+
In case of `latest`, the latest distribution matching the `condition` will be selected.
131+
132+
In case of `first`, the first distribution matching the `condition` will be selected.
133+
134+
The condition consists of a commpara separated list of semver version comparisons supporting `<`, `<=`, `>`, `>=`, `==`, `!=`. Examples:
135+
136+
- `latest:<=20.1.0`
137+
- `latest:<=20.1.0,>17.0.4,!=19.1.7`
138+
- `first:>=15.0.6,<16`
139+
126140
### Selecting Toolchains
127141

128142
If toolchains are registered (see Quickstart section above), you do not need to

toolchain/internal/BUILD.bazel

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
16-
load("llvm_distributions.bzl", "distributions_test_writer", "requirements_test_writer")
16+
load("llvm_distributions.bzl", "requirements_test_writer", "write_distributions")
1717

1818
exports_files(["template.modulemap"])
1919

@@ -25,14 +25,6 @@ write_distributions(
2525
visibility = ["//visibility:private"],
2626
)
2727

28-
requirements_test_writer(
29-
name = "llvm_requirements_test_output",
30-
testonly = True,
31-
output = "llvm_distributions.out.txt",
32-
select = "llvm_distributions.sel.txt",
33-
visibility = ["//visibility:private"],
34-
)
35-
3628
diff_test(
3729
name = "llvm_distributions_output_test",
3830
file1 = "llvm_distributions.golden.out.txt",
@@ -58,3 +50,16 @@ sh_test(
5850
"//conditions:default": ["@platforms//:incompatible"],
5951
}),
6052
)
53+
54+
requirements_test_writer(
55+
name = "llvm_requirements_test_output",
56+
testonly = True,
57+
result = "llvm_requirements_test.output.txt",
58+
visibility = ["//visibility:private"],
59+
)
60+
61+
diff_test(
62+
name = "llvm_requirements_test",
63+
file1 = "llvm_requirements_test.golden.txt",
64+
file2 = "llvm_requirements_test.output.txt",
65+
)

toolchain/internal/llvm_distributions.bzl

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "read_netrc", "use_netrc")
16+
load("@helly25_bzl//bzl/versions:versions.bzl", "versions")
1617
load(
1718
"//toolchain/internal:common.bzl",
1819
"attr_dict",
@@ -978,14 +979,59 @@ def _find_llvm_basename_or_error(llvm_version, all_llvm_distributions, host_info
978979

979980
return basenames[0], None
980981

982+
def _parse_version_requirements(version_requirements):
983+
if version_requirements in ["latest", "first"]:
984+
return []
985+
for prefix in ["latest:", "first:"]:
986+
if version_requirements.startswith(prefix):
987+
return versions.parse_requirements(version_requirements.removeprefix(prefix))
988+
fail("ERROR: Invalid version requirements: '{version_requirements}'.".format(
989+
version_requirements = version_requirements,
990+
))
991+
992+
def _get_llvm_versions(*, version_requirements, all_llvm_distributions):
993+
llvm_versions = {}
994+
for distribution in all_llvm_distributions.keys():
995+
version = distribution.split("-")[1]
996+
llvm_versions[version] = None
997+
if version_requirements.startswith("latest:"):
998+
return reversed(llvm_versions.keys())
999+
else:
1000+
return llvm_versions.keys()
1001+
1002+
def _required_llvm_release_name(*, version_requirements, all_llvm_distributions, host_info):
1003+
llvm_versions = _get_llvm_versions(version_requirements = version_requirements, all_llvm_distributions = all_llvm_distributions)
1004+
requires = _parse_version_requirements(version_requirements)
1005+
for llvm_version in llvm_versions:
1006+
if not versions.check_all_requirements(llvm_version, requires):
1007+
continue
1008+
basenames = _find_llvm_basename_list(llvm_version, all_llvm_distributions, host_info)
1009+
if len(basenames) == 1:
1010+
return llvm_version, basenames[0], None
1011+
return None, None, "ERROR: No matching distribution found."
1012+
1013+
def _contains_any(str, chars):
1014+
for c in chars:
1015+
if c in str:
1016+
return True
1017+
return False
1018+
9811019
def _distribution_urls(rctx):
9821020
"""Return LLVM `urls`, `shha256` and `strip_prefix` for the given context."""
9831021
llvm_version = _get_llvm_version(rctx)
9841022
all_llvm_distributions = _get_all_llvm_distributions(rctx)
9851023
_, sha256, strip_prefix, _ = _key_attrs(rctx)
9861024

9871025
if rctx.attr.distribution == "auto":
988-
basename, error = _find_llvm_basename_or_error(llvm_version, all_llvm_distributions, host_info(rctx))
1026+
rctx_host_info = host_info(rctx)
1027+
if _contains_any(llvm_version, ",:<>!="):
1028+
llvm_version, basename, error = _required_llvm_release_name(
1029+
version_requirements = _parse_version_requirements(llvm_version),
1030+
all_llvm_distributions = all_llvm_distributions,
1031+
host_info = rctx_host_info,
1032+
)
1033+
else:
1034+
basename, error = _find_llvm_basename_or_error(llvm_version, all_llvm_distributions, rctx_host_info)
9891035
if error:
9901036
fail(error)
9911037
if sha256 and sha256 != all_llvm_distributions[basename]:
@@ -1032,7 +1078,7 @@ def _parse_version(v):
10321078
def _version_string(version):
10331079
return ".".join([str(v) for v in version])
10341080

1035-
def _distributions_test_writer_impl(ctx):
1081+
def _write_distributions_impl(ctx):
10361082
"""Analyze the configured versions and write to a file for test consumption.
10371083
10381084
The test generated file '<rule_name>.out' contains the following lines:
@@ -1221,3 +1267,80 @@ write_distributions = rule(
12211267
"select": attr.output(mandatory = True),
12221268
},
12231269
)
1270+
1271+
def _requirements_test_writer_impl(ctx):
1272+
"""Analyze the configured versions and write to a file for test consumption.
1273+
The test generated file '<rule_name>.out' contains the following lines:
1274+
[<arch>,<os>,<requirement>]: <llvm_distribution_basename>
1275+
"""
1276+
all_llvm_distributions = _llvm_distributions
1277+
requirement_list = [
1278+
"latest:<=20.1.0",
1279+
"latest:<=20.1.0,>17.0.4,!=19.1.7",
1280+
"latest:<20.1.0,>17.0.4,!=19.1.7",
1281+
"latest:<20.1.0,>17.0.4",
1282+
"latest:>=15.0.6,<16",
1283+
"first:>=15.0.6,<16",
1284+
]
1285+
arch_list = [
1286+
"aarch64",
1287+
"armv7a",
1288+
"x86_64",
1289+
]
1290+
os_list = [
1291+
"darwin",
1292+
"linux",
1293+
"windows",
1294+
]
1295+
ANY_VERSION = "0" # Version does not matter, but must be a valid integer
1296+
dist_dict_list = {
1297+
"linux": [
1298+
# keep sorted
1299+
struct(name = "ubuntu", version = ANY_VERSION),
1300+
struct(name = "raspbian", version = ANY_VERSION),
1301+
struct(name = "rhel", version = ANY_VERSION),
1302+
],
1303+
}
1304+
result = []
1305+
for arch in arch_list:
1306+
for os in os_list:
1307+
dist_list = dist_dict_list.get(os, [struct(name = os, version = "")])
1308+
for dist in dist_list:
1309+
for requirement in requirement_list:
1310+
host_info = struct(
1311+
arch = arch,
1312+
os = os,
1313+
dist = dist,
1314+
)
1315+
llvm_version, basename, error = _required_llvm_release_name(
1316+
version_requirements = requirement,
1317+
all_llvm_distributions = all_llvm_distributions,
1318+
host_info = host_info,
1319+
)
1320+
if llvm_version and basename:
1321+
result.append("[{arch},{os}{dist_name}{dist_version},'{requirement}']: {llvm_version} = {basename}".format(
1322+
arch = arch,
1323+
os = os,
1324+
dist_name = "," + dist.name if os == "linux" else "",
1325+
dist_version = "," + dist.version if os == "linux" else "",
1326+
requirement = requirement,
1327+
llvm_version = llvm_version,
1328+
basename = basename,
1329+
))
1330+
else:
1331+
result.append("[{arch},{os},\"{requirement}\"]: {error}".format(
1332+
arch = arch,
1333+
os = os,
1334+
requirement = requirement,
1335+
llvm_version = llvm_version,
1336+
basename = basename,
1337+
error = error or "ERROR: N/A",
1338+
))
1339+
ctx.actions.write(ctx.outputs.result, "\n".join(result) + "\n")
1340+
1341+
requirements_test_writer = rule(
1342+
implementation = _requirements_test_writer_impl,
1343+
attrs = {
1344+
"result": attr.output(mandatory = True),
1345+
},
1346+
)

0 commit comments

Comments
 (0)