Skip to content

Commit 60c9695

Browse files
author
Marcus Boerger
authored
Verify the current resolution (#490)
* Split `llvm_release_name` into two: * `llvm_release_name_host_info`: Used to verify the resolution. It does not take a rctx. * `llvm_release_name_context`: Will fail if resolution from rctx fails. * Change `dist_version_arch` to `host_info` which returns a struct, so the result can easily be extended and only one call is needed. As can be seen in `toolchain/internal/llvm_distributions.golden.sel.txt` there are a number of decisions taken in mode `auto` that lead to failures in predicting a correct LLVM distribution. Those we should fix.
1 parent 188c31a commit 60c9695

File tree

8 files changed

+22449
-132
lines changed

8 files changed

+22449
-132
lines changed

toolchain/internal/BUILD.bazel

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,33 @@ exports_files(["template.modulemap"])
2020
write_distributions(
2121
name = "llvm_distributions",
2222
testonly = True,
23+
output = "llvm_distributions.out.txt",
24+
select = "llvm_distributions.sel.txt",
2325
visibility = ["//visibility:private"],
2426
)
2527

2628
diff_test(
27-
name = "llvm_distributions_test",
28-
file1 = "llvm_distributions.golden.txt",
29-
file2 = "llvm_distributions",
29+
name = "llvm_distributions_output_test",
30+
file1 = "llvm_distributions.golden.out.txt",
31+
file2 = "llvm_distributions.out.txt",
32+
)
33+
34+
diff_test(
35+
name = "llvm_distributions_select_test",
36+
file1 = "llvm_distributions.golden.sel.txt",
37+
file2 = "llvm_distributions.sel.txt",
38+
)
39+
40+
sh_test(
41+
name = "llvm_distributions_select_no_error_test",
42+
srcs = ["llvm_distributions_select_no_error_test.sh"],
43+
data = [
44+
"llvm_distributions.golden.sel.txt",
45+
"llvm_distributions.sel.txt",
46+
],
47+
target_compatible_with = select({
48+
"@platforms//os:osx": [],
49+
"@platforms//os:linux": [],
50+
"//conditions:default": ["@platforms//:incompatible"],
51+
}),
3052
)

toolchain/internal/common.bzl

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ _toolchain_tools_darwin = {
5555
}
5656

5757
def exec_os_key(rctx):
58-
(os, version, arch) = dist_version_arch(rctx)
59-
if version == "":
60-
return "%s-%s" % (os, arch)
58+
info = host_info(rctx)
59+
if info.dist.version == "":
60+
return "%s-%s" % (info.os, info.arch)
6161
else:
62-
return "%s-%s-%s" % (os, version, arch)
62+
return "%s-%s-%s" % (info.dist.name, info.dist.version, info.arch)
6363

6464
_known_distros = [
6565
# keep sorted
@@ -103,15 +103,23 @@ def _linux_dist(rctx):
103103

104104
return distname, version
105105

106-
def dist_version_arch(rctx):
106+
def host_info(rctx):
107107
_os = os(rctx)
108108
_arch = arch(rctx)
109109

110110
if _os == "linux" and not rctx.attr.exec_os:
111-
(distname, version) = _linux_dist(rctx)
112-
return distname, version, _arch
113-
114-
return _os, "", _arch
111+
dist_name, dist_version = _linux_dist(rctx)
112+
else:
113+
dist_name = os
114+
dist_version = ""
115+
return struct(
116+
arch = _arch,
117+
dist = struct(
118+
name = dist_name,
119+
version = dist_version,
120+
),
121+
os = _os,
122+
)
115123

116124
def os(rctx):
117125
# Less granular host OS name, e.g. linux.

toolchain/internal/llvm_distributions.bzl

Lines changed: 150 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,17 @@
1313
# limitations under the License.
1414

1515
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "read_netrc", "use_netrc")
16-
load("//toolchain/internal:common.bzl", _arch = "arch", _attr_dict = "attr_dict", _exec_os_arch_dict_value = "exec_os_arch_dict_value", _os = "os")
17-
load("//toolchain/internal:release_name.bzl", _llvm_release_name = "llvm_release_name", _llvm_release_name_19 = "llvm_release_name_19")
16+
load(
17+
"//toolchain/internal:common.bzl",
18+
"attr_dict",
19+
"exec_os_arch_dict_value",
20+
"host_info",
21+
)
22+
load(
23+
"//toolchain/internal:release_name.bzl",
24+
"llvm_release_name_context",
25+
"llvm_release_name_host_info",
26+
)
1827

1928
# If a new LLVM version is missing from this list, please add the shasums here
2029
# and the new version in toolchain/internal/llvm_distributions.golden.txt.
@@ -672,6 +681,7 @@ def _get_auth(ctx, urls):
672681
return {}
673682

674683
def download_llvm(rctx):
684+
"""Download the LLVM distribution for the given context."""
675685
urls = []
676686
sha256 = None
677687
strip_prefix = None
@@ -699,13 +709,13 @@ def download_llvm(rctx):
699709
lib_path = clang_version.get_child("lib", lib_name)
700710
rctx.file(lib_path, libclang_rt_content, legacy_utf8 = False)
701711

702-
updated_attrs = _attr_dict(rctx.attr)
712+
updated_attrs = attr_dict(rctx.attr)
703713
if update_sha256:
704714
updated_attrs["sha256"].update([(key, res.sha256)])
705715
return updated_attrs
706716

707717
def _urls(rctx):
708-
(key, urls) = _exec_os_arch_dict_value(rctx, "urls", debug = False)
718+
(key, urls) = exec_os_arch_dict_value(rctx, "urls", debug = False)
709719
if not urls:
710720
print("LLVM archive URLs missing and no default fallback provided; will try 'distribution' attribute") # buildifier: disable=print
711721

@@ -719,23 +729,31 @@ def _get_llvm_version(rctx):
719729
return rctx.attr.llvm_version
720730
if not rctx.attr.llvm_versions:
721731
fail("Neither 'llvm_version' nor 'llvm_versions' given.")
722-
(_, llvm_version) = _exec_os_arch_dict_value(rctx, "llvm_versions")
732+
(_, llvm_version) = exec_os_arch_dict_value(rctx, "llvm_versions")
723733
if not llvm_version:
724-
fail("LLVM version string missing for ({os}, {arch})", os = _os(rctx), arch = _arch(rctx))
734+
info = host_info(rctx)
735+
fail(
736+
"LLVM version string missing for ({os}/{dist_name}/{dist_verison}, {arch})",
737+
os = info.os,
738+
dist_name = info.dist.name,
739+
dist_version = info.dist.version,
740+
arch = info.arch,
741+
)
725742
return llvm_version
726743

727-
def _find_llvm_basename_list(llvm_version, arch, os):
744+
def _find_llvm_basename_list(llvm_version, host_info):
728745
"""Lookup (llvm_version, arch, os) in the list of basenames in `_llvm_distributions.`"""
729-
name = _llvm_release_name_19(llvm_version, arch, os)
746+
name, _ = llvm_release_name_host_info(llvm_version, host_info)
730747
if name in _llvm_distributions:
731748
return [name]
732749
return []
733750

734751
def _distribution_urls(rctx):
752+
"""Return LLVM `urls`, `shha256` and `strip_prefix` for the given context."""
735753
llvm_version = _get_llvm_version(rctx)
736754

737755
if rctx.attr.distribution == "auto":
738-
basename = _llvm_release_name(rctx, llvm_version)
756+
basename = llvm_release_name_context(rctx, llvm_version)
739757
else:
740758
basename = rctx.attr.distribution
741759

@@ -769,8 +787,8 @@ def _distribution_urls(rctx):
769787
def _parse_version(v):
770788
return tuple([int(s) for s in v.split(".")])
771789

772-
def _version_ge(lhs, rhs):
773-
return _parse_version(lhs) >= _parse_version(rhs)
790+
def _version_string(version):
791+
return ".".join([str(v) for v in version])
774792

775793
def _write_distributions_impl(ctx):
776794
"""Analyze the configured versions and write to a file for test consumption.
@@ -785,70 +803,167 @@ def _write_distributions_impl(ctx):
785803
"""
786804
arch_list = [
787805
"aarch64",
806+
"armv7a",
807+
"mips",
808+
"mipsel",
809+
"powerpc64",
810+
"powerpc64le",
811+
"sparcv9",
812+
"x86_32",
788813
"x86_64",
789814
]
790815
os_list = [
791816
"darwin",
792817
"linux",
793-
"raspbian",
794818
"windows",
795819
]
820+
ANY_VERSION = "0" # Version does not matter, but must be a valid integer
821+
dist_dict_list = {
822+
"linux": [
823+
# struct(name = "ibm-aix", version = "7.2"), unreachable
824+
# keep sorted
825+
struct(name = "amzn", version = ANY_VERSION),
826+
struct(name = "arch", version = ANY_VERSION),
827+
struct(name = "centos", version = "6"),
828+
struct(name = "centos", version = "7"),
829+
struct(name = "debian", version = "0"),
830+
struct(name = "debian", version = "8"),
831+
struct(name = "debian", version = "9"),
832+
struct(name = "fedora", version = "26"),
833+
struct(name = "fedora", version = "27"),
834+
struct(name = "fedora", version = "42"),
835+
struct(name = "freebsd", version = "10"),
836+
struct(name = "freebsd", version = "11"),
837+
struct(name = "freebsd", version = "12"),
838+
struct(name = "freebsd", version = "13"),
839+
struct(name = "linuxmint", version = "18"),
840+
struct(name = "linuxmint", version = "19"),
841+
struct(name = "pc-solaris", version = "2.11"),
842+
struct(name = "raspbian", version = ANY_VERSION),
843+
struct(name = "rhel", version = ANY_VERSION),
844+
struct(name = "sun-solaris", version = "2.11"),
845+
struct(name = "suse", version = "11.3"),
846+
struct(name = "suse", version = "12.2"),
847+
struct(name = "suse", version = "12.3"),
848+
struct(name = "suse", version = "12.4"),
849+
struct(name = "suse", version = "15.5"),
850+
struct(name = "suse", version = "16.0"),
851+
struct(name = "suse", version = "17.0"),
852+
struct(name = "ubuntu", version = "14.04"),
853+
struct(name = "ubuntu", version = "16.04"),
854+
struct(name = "ubuntu", version = "18.04.5"),
855+
struct(name = "ubuntu", version = "18.04.6"),
856+
struct(name = "ubuntu", version = "18.04"),
857+
struct(name = "ubuntu", version = "20.04"),
858+
struct(name = "ubuntu", version = "20.10"),
859+
struct(name = "ubuntu", version = "22.04"),
860+
struct(name = "ubuntu", version = "24.04"),
861+
],
862+
}
796863

797864
# Compute all unique version strings starting with `MIN_VERSION`.
798-
MIN_VERSION = "19.0.0"
865+
MIN_VERSION = _parse_version("6.0.0")
866+
MAX_VERSION = _parse_version("20.1.3")
799867
version_list = []
800868
for name in _llvm_distributions.keys():
801869
for prefix in ["LLVM-", "clang+llvm-"]:
802870
if name.startswith(prefix):
803-
version = name.split("-", 2)[1]
804-
if not _version_ge(version, MIN_VERSION):
805-
continue
806-
version_list.append(version)
807-
for version in _llvm_distributions_base_url.keys():
808-
if not _version_ge(version, MIN_VERSION):
809-
continue
810-
version_list.append(version)
871+
version = _parse_version(name.split("-", 2)[1])
872+
if version >= MIN_VERSION:
873+
version_list.append(version)
874+
break
875+
for v in _llvm_distributions_base_url.keys():
876+
version = _parse_version(v)
877+
if version >= MIN_VERSION:
878+
version_list.append(version)
811879
versions = {v: None for v in version_list}
812880

813881
# Write versions to output to check which versions we take into account.
814882
output = []
883+
select = []
815884
for version in versions.keys():
816-
output.append("version: " + version)
885+
output.append("version: " + _version_string(version))
817886

818887
# We keep track of versions in `not_found` and remove the ones we found.
819888
# So at the end all version that were not found remain, hence the name.
820889
not_found = {
821890
k: v
822891
for k, v in _llvm_distributions.items()
823-
if _version_ge(k.split("-")[1], MIN_VERSION)
892+
if _parse_version(k.split("-")[1]) >= MIN_VERSION
824893
}
825894

826895
# While computing we add predicted versions that are not configured as True.
827896
# At the end we add the not-found versions as False.
828897
result = {}
829898

899+
# Collect cases that produce duplicates (or multiple) basenames.
900+
dupes = []
901+
830902
# For all versions X arch X os check if we can compute the distribution.
831903
for version in versions.keys():
832904
for arch in arch_list:
833905
for os in os_list:
834-
basenames = _find_llvm_basename_list(version, arch, os)
835-
if len(basenames) != 1:
836-
continue
837-
basename = basenames[0]
838-
if basename in _llvm_distributions:
839-
if basename in not_found:
840-
not_found.pop(basename)
841-
else:
842-
result[basename] = True
906+
dist_list = dist_dict_list.get(os, [struct(name = os, version = "")])
907+
for dist in dist_list:
908+
host_info = struct(
909+
arch = arch,
910+
os = os,
911+
dist = dist,
912+
)
913+
basenames = _find_llvm_basename_list(_version_string(version), host_info)
914+
if version <= MAX_VERSION:
915+
predicted, error = llvm_release_name_host_info(
916+
_version_string(version),
917+
host_info,
918+
)
919+
if not error:
920+
if predicted.endswith(".exe"):
921+
error = "ERROR: Windows .exe is not supported: " + predicted
922+
elif predicted not in _llvm_distributions:
923+
error = "ERROR: Unavailable prediction: " + predicted
924+
else:
925+
arch_found = [arch for arch in arch_list if arch in predicted]
926+
if len(arch_found) == 1 and arch_found[0] != arch:
927+
error = "ERROR: Bad arch selection: " + predicted
928+
select.append("{version}-{arch}-{os}/{dist_name}/{dist_version} -> {basename}".format(
929+
version = _version_string(version),
930+
arch = arch,
931+
os = os,
932+
dist_name = dist.name,
933+
dist_version = dist.version,
934+
basename = error or predicted,
935+
))
936+
if len(basenames) != 1:
937+
if basenames:
938+
dupes.append("dup: {version}-{arch}-{os}-{dist_name}-{dist_version} -> {count}".format(
939+
version = _version_string(version),
940+
arch = arch,
941+
os = os,
942+
dist_name = dist.name,
943+
dist_version = dist.version,
944+
count = len(basenames),
945+
))
946+
dupes.extend([" : " + basename for basename in basenames])
947+
continue
948+
basename = basenames[0]
949+
if basename in _llvm_distributions:
950+
if basename in not_found:
951+
not_found.pop(basename)
952+
else:
953+
result[basename] = True
843954

844955
# Build result
845956
for dist in not_found:
846957
result[dist] = False
847958
output += [("add: " if found else "del: ") + dist for dist, found in result.items()]
848-
out = ctx.actions.declare_file(ctx.label.name + ".out")
849-
ctx.actions.write(out, "\n".join(output) + "\n")
850-
return [DefaultInfo(files = depset([out]))]
959+
output += dupes
960+
ctx.actions.write(ctx.outputs.output, "\n".join(output) + "\n")
961+
ctx.actions.write(ctx.outputs.select, "\n".join(select) + "\n")
851962

852963
write_distributions = rule(
853964
implementation = _write_distributions_impl,
965+
attrs = {
966+
"output": attr.output(mandatory = True),
967+
"select": attr.output(mandatory = True),
968+
},
854969
)

0 commit comments

Comments
 (0)