Skip to content

Commit eac33e3

Browse files
authored
Fix platform-specific linker resolution (#872)
Allow one toolchain configuration to select catalogued linkers on some target platforms and native or bundled linkers on others. This makes configurations such as Linux mold plus macOS `auto` work as intended. Fixes #871. Details: - preserve target keys while materializing catalogued linker distributions; - skip a catalogue artifact unavailable on the execution platform when it belongs only to targets for another OS; - omit those unusable cross-target toolchain configurations rather than breaking valid native targets; - apply `linker_version` requirements only to catalogue selections, ignoring them for `auto`, absolute paths, and the LLVM-distribution linker; - document and test the inherited-version behavior. Validation: - `bazel test //...` - `trunk check` - xff macOS build with one toolchain selecting `auto` on Darwin and latest mold on Linux
1 parent 58316fe commit eac33e3

5 files changed

Lines changed: 35 additions & 16 deletions

File tree

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,9 @@ map. Setting both `linker_version` and `linker_versions` is an error.
578578
catalogue. To use an already installed mold instead of downloading one, provide
579579
its absolute path, for example `linker = {"": "/usr/bin/mold"}`. The `auto`
580580
selection resolves the platform's native `ld`; it does not search for mold.
581+
Version requirements apply only to catalogue selections. They are ignored for
582+
`auto`, absolute paths, and the linker included in the LLVM distribution, so a
583+
default `linker_version` can be combined with platform-specific overrides.
581584

582585
##### Optional mold module version inference
583586

‎toolchain/internal/configure.bzl‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,25 +485,22 @@ def _linker_descriptor(rctx, selection, version, linker_paths, exec_os, target_o
485485
)
486486

487487
if selection == "auto":
488-
if version:
489-
fail("linker version '{}' cannot be used with linker selection 'auto'".format(version))
490488
if exec_os != target_os:
491489
fail("linker selection 'auto' requires matching execution and target operating systems, got {} -> {}".format(exec_os, target_os))
492490
selection = _native_linker(rctx, exec_os)
493491
elif not _is_absolute_path(selection):
494492
reference = "{}@{}".format(selection, version) if version else selection
495493
linker_info = linker_paths.get(reference)
496494
if not linker_info:
495+
if exec_os != target_os:
496+
return None
497497
fail("catalogued linker '{}' was not materialized".format(reference))
498498
return struct(
499499
file = linker_info.path,
500500
path = linker_info.path,
501501
supports_start_end_lib = "start_end_lib" in linker_info.features,
502502
)
503503

504-
if version:
505-
fail("linker version '{}' cannot be used with absolute linker path '{}'".format(version, selection))
506-
507504
linker_path = rctx.path(selection)
508505
if not linker_path.exists:
509506
fail("configured linker does not exist: {}".format(selection))
@@ -578,6 +575,8 @@ def _cc_toolchain_str(
578575
exec_os,
579576
target_os,
580577
)
578+
if linker == None:
579+
return ""
581580

582581
extra_files_str = repr(":internal-use-tools" if bazel_features.rules.merkle_cache_v2 else ":internal-use-tools-legacy")
583582

‎toolchain/internal/linker_distributions.bzl‎

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,11 @@ load("//toolchain/internal:common.bzl", "is_absolute_path")
1818
load("//toolchain/internal:distributions_repo.bzl", "load_jsonc")
1919
load("//toolchain/internal:llvm_distributions.bzl", "resolve_version")
2020

21-
def catalogued_linker_reference(selection, version, target = ""):
21+
def catalogued_linker_reference(selection, version):
2222
"""Return the catalogue reference for a linker selection, or None."""
2323
if not selection:
24-
if version:
25-
fail("linker version '{}' has no linker selection for target '{}'".format(version, target))
2624
return None
2725
if selection == "auto" or is_absolute_path(selection):
28-
if version:
29-
fail("linker version '{}' cannot be used with linker selection '{}' for target '{}'".format(version, selection, target))
3026
return None
3127
return "{}@{}".format(selection, version) if version else selection
3228

@@ -116,11 +112,27 @@ def _linker_distributions_repository_impl(rctx):
116112
platform = "{}-{}".format(_normalize_os(rctx), _normalize_arch(rctx))
117113
manifest = {}
118114

119-
for index, reference in enumerate(sorted(rctx.attr.linkers)):
115+
references = {}
116+
for target, reference in rctx.attr.linkers.items():
117+
references.setdefault(reference, []).append(target)
118+
119+
for index, reference in enumerate(sorted(references.keys())):
120120
if reference == "mold":
121121
linker, version_selection = "mold", _mold_version(rctx)
122122
else:
123123
linker, version_selection = _parse_reference(reference)
124+
versions = catalogue.get(linker)
125+
if not versions:
126+
fail("unknown linker '{}'; known linkers: {}".format(linker, ", ".join(sorted(catalogue.keys()))))
127+
available_platforms = {
128+
available_platform: True
129+
for platforms in versions.values()
130+
for available_platform in platforms.keys()
131+
}
132+
if platform not in available_platforms:
133+
exec_os = platform.split("-")[0]
134+
if all([target and target.split("-")[0] != exec_os for target in references[reference]]):
135+
continue
124136
_, distribution = resolve_linker_distribution(catalogue, linker, version_selection, platform)
125137

126138
extraction_dir = "_extract/{}".format(index)
@@ -159,7 +171,7 @@ linker_distributions_repository = repository_rule(
159171
"exec_arch": attr.string(),
160172
"exec_os": attr.string(),
161173
"extra_catalogues": attr.label_list(allow_files = [".json", ".jsonc"]),
162-
"linkers": attr.string_list(mandatory = True),
174+
"linkers": attr.string_dict(mandatory = True),
163175
"mold_source": attr.label(allow_single_file = True),
164176
"use_builtin_catalogue": attr.bool(default = True),
165177
},
@@ -186,8 +198,11 @@ def _linker_version_test_writer_impl(ctx):
186198
("lld", "19.1.7"),
187199
("mold", ""),
188200
("auto", ""),
201+
("auto", "latest"),
189202
("/usr/bin/ld", ""),
203+
("/usr/bin/ld", "latest"),
190204
("", ""),
205+
("", "latest"),
191206
]
192207
]
193208
catalogue = {

‎toolchain/internal/linker_version_test.golden.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ gold + 1.2.3 -> gold@1.2.3
88
lld + 19.1.7 -> lld@19.1.7
99
mold + <empty> -> mold
1010
auto + <empty> -> None
11+
auto + latest -> None
1112
/usr/bin/ld + <empty> -> None
13+
/usr/bin/ld + latest -> None
1214
<empty> + <empty> -> None
15+
<empty> + latest -> None
1316
latest linux-x86_64 -> 3.0.0 False
1417
latest darwin-aarch64 -> 2.0.0 True
1518
merged override -> override

‎toolchain/rules.bzl‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,17 @@ def llvm_toolchain(name, **kwargs):
7070
for target in targets.keys():
7171
selection = linkers.get(target, linkers.get("", ""))
7272
version = linker_versions.get(target, linker_versions.get("", ""))
73-
reference = catalogued_linker_reference(selection, version, target)
73+
reference = catalogued_linker_reference(selection, version)
7474
if reference:
75-
catalogued_linkers[reference] = True
76-
catalogued_linkers = sorted(catalogued_linkers.keys())
75+
catalogued_linkers[target] = reference
7776
if catalogued_linkers:
7877
linker_distributions_repository(
7978
name = name + "_linkers",
8079
exec_arch = kwargs.get("exec_arch", ""),
8180
exec_os = kwargs.get("exec_os", ""),
8281
extra_catalogues = kwargs.get("extra_linker_distribution_files", []),
8382
linkers = catalogued_linkers,
84-
mold_source = "@mold//:src/entry.cc" if "mold" in catalogued_linkers else None,
83+
mold_source = "@mold//:src/entry.cc" if "mold" in catalogued_linkers.values() else None,
8584
use_builtin_catalogue = kwargs.get("use_builtin_linker_distributions", True),
8685
)
8786
kwargs["linker_repository"] = "@{}_linkers//:linkers.json".format(name)

0 commit comments

Comments
 (0)