diff --git a/rs/toolchains/BUILD.bazel b/rs/toolchains/BUILD.bazel index e6bf17e..da286cc 100644 --- a/rs/toolchains/BUILD.bazel +++ b/rs/toolchains/BUILD.bazel @@ -1,7 +1,10 @@ load("@bazel_lib//:bzl_library.bzl", "bzl_library") +load(":stdlib_linkflags_test.bzl", "stdlib_linkflags_test") package(default_visibility = ["//visibility:public"]) +stdlib_linkflags_test(name = "stdlib_linkflags_target_triples_test") + alias( name = "all_targets", actual = select({ @@ -97,6 +100,12 @@ bzl_library( ], ) +bzl_library( + name = "stdlib_linkflags_test", + srcs = ["stdlib_linkflags_test.bzl"], + deps = ["@bazel_skylib//lib:unittest"], +) + bzl_library( name = "toolchain_utils", srcs = ["toolchain_utils.bzl"], diff --git a/rs/toolchains/declare_rustc_toolchains.bzl b/rs/toolchains/declare_rustc_toolchains.bzl index 2295f42..b2440c4 100644 --- a/rs/toolchains/declare_rustc_toolchains.bzl +++ b/rs/toolchains/declare_rustc_toolchains.bzl @@ -20,6 +20,44 @@ def _rustc_flags_to_select(rustc_flags_by_triple): {"//conditions:default": []}, ) +def _stdlib_linkflags_for_target_triple(target_triple): + target = _parse_triple(target_triple) + + if target.system == "android": + return ["-ldl", "-llog"] + + if target.system == "freebsd": + return ["-lexecinfo", "-lpthread"] + + if target.system == "ios": + return ["-lSystem", "-lobjc", "-Wl,-framework,Security", "-Wl,-framework,Foundation", "-lresolv"] + + if target.system == "macos": + return ["-lSystem", "-lresolv"] + + if target.system == "netbsd": + return ["-lpthread", "-lrt"] + + if target.system == "nixos": + return ["-ldl", "-lpthread"] + + if target.system == "openbsd": + return ["-lpthread"] + + if target.system == "windows" and target.abi in ("gnu", "gnullvm"): + return ["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"] + + if target.system == "windows" and target.abi == "msvc": + return ["advapi32.lib", "ws2_32.lib", "userenv.lib", "Bcrypt.lib"] + + return [] + +def _stdlib_linkflags_to_select(targets): + return select( + {"@rules_rs//rs/platforms/config:" + target: _stdlib_linkflags_for_target_triple(target) for target in targets} | + {"//conditions:default": []}, + ) + def _component(component, triple, default): component = component.get(triple) if type(component) == "dict" else component return component or rust_toolchain_component_label(default) @@ -170,24 +208,7 @@ def declare_rustc_toolchains( "@platforms//os:windows": ".dll", "//conditions:default": ".so", }), - stdlib_linkflags = select({ - "@platforms//os:android": ["-ldl", "-llog"], - "@platforms//os:freebsd": ["-lexecinfo", "-lpthread"], - "@platforms//os:macos": ["-lSystem", "-lresolv"], - "@platforms//os:netbsd": ["-lpthread", "-lrt"], - "@platforms//os:nixos": ["-ldl", "-lpthread"], - "@platforms//os:openbsd": ["-lpthread"], - "@platforms//os:ios": ["-lSystem", "-lobjc", "-Wl,-framework,Security", "-Wl,-framework,Foundation", "-lresolv"], - "@llvm//constraints/windows/abi:gnu": ["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"], - "@llvm//constraints/windows/abi:gnullvm": ["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"], - "@llvm//constraints/windows/abi:msvc": [ - "advapi32.lib", - "ws2_32.lib", - "userenv.lib", - "Bcrypt.lib", - ], - "//conditions:default": [], - }), + stdlib_linkflags = _stdlib_linkflags_to_select(target_triples), default_edition = edition, extra_exec_rustc_flags = _rustc_flags_to_select(extra_exec_rustc_flags), extra_rustc_flags = _rustc_flags_to_select(extra_rustc_flags), diff --git a/rs/toolchains/stdlib_linkflags_test.bzl b/rs/toolchains/stdlib_linkflags_test.bzl new file mode 100644 index 0000000..50b68e0 --- /dev/null +++ b/rs/toolchains/stdlib_linkflags_test.bzl @@ -0,0 +1,79 @@ +"""Checks that standard-library link flags follow the resolved Rust target.""" + +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") + +_WINDOWS_GNU_FLAGS = ["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"] +_CASES = { + "linux_with_msvc_abi": ("x86_64-unknown-linux-gnu", "msvc", []), + "macos_with_gnu_abi": ("aarch64-apple-darwin", "gnu", ["-lSystem", "-lresolv"]), + "windows_msvc": ("aarch64-pc-windows-msvc", None, ["advapi32.lib", "ws2_32.lib", "userenv.lib", "Bcrypt.lib"]), + "windows_gnu": ("x86_64-pc-windows-gnu", None, _WINDOWS_GNU_FLAGS), + "windows_gnullvm": ("aarch64-pc-windows-gnullvm", None, _WINDOWS_GNU_FLAGS), +} + +_StdlibInfo = provider( + "Resolved Rust target and standard-library link flags.", + fields = { + "triple": "Resolved Rust target triple.", + "flags": "Native linker flags required by the Rust standard library.", + }, +) + +def _stdlib_info_impl(ctx): + toolchain = ctx.toolchains["@rules_rust//rust:toolchain_type"] + return [_StdlibInfo( + triple = toolchain.target_triple.str, + flags = [ + flag + for linker_input in toolchain.stdlib_linkflags.linking_context.linker_inputs.to_list() + for flag in linker_input.user_link_flags + ], + )] + +_stdlib_info = rule( + implementation = _stdlib_info_impl, + toolchains = ["@rules_rust//rust:toolchain_type"], +) + +def _target_platforms_impl(_settings, _attr): + return { + name: {"//command_line_option:platforms": [str(Label("//rs/toolchains:stdlib_" + name))]} + for name in _CASES + } + +_target_platforms = transition( + implementation = _target_platforms_impl, + inputs = [], + outputs = ["//command_line_option:platforms"], +) + +def _stdlib_linkflags_test_impl(ctx): + env = unittest.begin(ctx) + for name, (triple, _, flags) in _CASES.items(): + actual = ctx.split_attr.target[name][_StdlibInfo] + asserts.equals(env, triple, actual.triple, name) + asserts.equals(env, flags, actual.flags, name) + return unittest.end(env) + +_stdlib_linkflags_test = unittest.make( + _stdlib_linkflags_test_impl, + attrs = { + "target": attr.label(cfg = _target_platforms, mandatory = True), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, +) + +def stdlib_linkflags_test(name): + for case, (triple, extra_abi, _) in _CASES.items(): + # ABI constraints are independent of the OS. They must not introduce + # Windows libraries or ambiguous selects on a non-Windows Rust target. + native.platform( + name = "stdlib_" + case, + parents = ["@rules_rs//rs/platforms:" + triple], + constraint_values = ["@llvm//constraints/windows/abi:" + extra_abi] if extra_abi else [], + ) + + _stdlib_info(name = "stdlib_info", tags = ["manual"]) + _stdlib_linkflags_test(name = name, target = ":stdlib_info")