mirror of https://github.com/bazelbuild/rules_rust
Have rust_test put its compilation outputs in a subdirectory (#1434)
This is supposed to fix the long standing Windows flakiness as described in https://github.com/bazelbuild/rules_rust/pull/1427#issuecomment-1171940333 Initially I thought it's an issue with `rustdoc_test`, however the actual issue is that `rust_binary` and its `rust_test` have the same crate name and generate the same intermediary `.o` files. For sandboxed builds this is not an issue, as the actions are isolated, however, we have a race condition in non-sandboxed builds (Windows): Let's consider the following build: ``` bazel build --spawn_strategy=standalone \ //test/unit/rustdoc:bin_with_transitive_proc_macro \ //test/unit/rustdoc:bin_with_transitive_proc_macro_test ``` The `bin_with_transitive_proc_macro` compile action will create the following intermediate file: `bazel-out/k8-fastbuild/bin/test/unit/rustdoc/bin_with_transitive_proc_macro.bin_with_transitive_proc_macro.573369dc-cgu.2.rcgu.o`, as will the `bin_with_transitive_proc_macro_test` action. These two files differ slightly (as the second action is for a test), namely the `bin_with_transitive_proc_macro` output has the following symbols: ``` 0000000000000000 T main 0000000000000000 t _ZN30bin_with_transitive_proc_macro4main17hfc292cc42314e131E U _ZN3std2rt10lang_start17h1dbc829c47cd61d9E ``` while the `bin_with_transitive_proc_macro_test` `.o` output looks like this: ``` 0000000000000000 T main 0000000000000000 t _ZN30bin_with_transitive_proc_macro4main17h28726504dc060f8dE U _ZN3std2rt10lang_start17h1dbc829c47cd61d9E U _ZN4test16test_main_static17h37e3d88407f5b40fE ``` Now, when the two actions run in parallel, it can happen that `bin_with_transitive_proc_macro` creates the output, and `bin_with_transitive_proc_macro_test` overwrites it. Then, at linking time for `bin_with_transitive_proc_macro`, `rustc` will complain: ``` note: ld.lld: error: undefined symbol: test::test_main_static::h37e3d88407f5b40f ``` This is because `bin_with_transitive_proc_macro` is not a test and as such is not linked against `libtest`. This PR fixes the issue by directing the compilation outputs of `rust_test` to be under a `test-{hash}` directory.
This commit is contained in:
parent
8b0f644122
commit
26344d4cd7
|
@ -384,13 +384,11 @@ def _rust_binary_impl(ctx):
|
|||
),
|
||||
)
|
||||
|
||||
def _rust_test_common(ctx, toolchain, output):
|
||||
"""Builds a Rust test binary.
|
||||
def _rust_test_impl(ctx):
|
||||
"""The implementation of the `rust_test` rule.
|
||||
|
||||
Args:
|
||||
ctx (ctx): The ctx object for the current target.
|
||||
toolchain (rust_toolchain): The current `rust_toolchain`
|
||||
output (File): The output File that will be produced, depends on crate type.
|
||||
|
||||
Returns:
|
||||
list: The list of providers. See `rustc_compile_action`
|
||||
|
@ -398,6 +396,8 @@ def _rust_test_common(ctx, toolchain, output):
|
|||
_assert_no_deprecated_attributes(ctx)
|
||||
_assert_correct_dep_mapping(ctx)
|
||||
|
||||
toolchain = find_toolchain(ctx)
|
||||
|
||||
srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None))
|
||||
crate_type = "bin"
|
||||
|
||||
|
@ -408,6 +408,15 @@ def _rust_test_common(ctx, toolchain, output):
|
|||
# Target is building the crate in `test` config
|
||||
crate = ctx.attr.crate[rust_common.crate_info] if rust_common.crate_info in ctx.attr.crate else ctx.attr.crate[rust_common.test_crate_info].crate
|
||||
|
||||
output_hash = determine_output_hash(crate.root, ctx.label)
|
||||
output = ctx.actions.declare_file(
|
||||
"test-%s/%s%s" % (
|
||||
output_hash,
|
||||
ctx.label.name,
|
||||
toolchain.binary_ext,
|
||||
),
|
||||
)
|
||||
|
||||
# Optionally join compile data
|
||||
if crate.compile_data:
|
||||
compile_data = depset(ctx.files.compile_data, transitive = [crate.compile_data])
|
||||
|
@ -435,6 +444,15 @@ def _rust_test_common(ctx, toolchain, output):
|
|||
if not crate_root:
|
||||
crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, "lib")
|
||||
|
||||
output_hash = determine_output_hash(crate_root, ctx.label)
|
||||
output = ctx.actions.declare_file(
|
||||
"test-%s/%s%s" % (
|
||||
output_hash,
|
||||
ctx.label.name,
|
||||
toolchain.binary_ext,
|
||||
),
|
||||
)
|
||||
|
||||
# Target is a standalone crate. Build the test binary as its own crate.
|
||||
crate_info = rust_common.create_crate_info(
|
||||
name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name),
|
||||
|
@ -476,23 +494,6 @@ def _rust_test_common(ctx, toolchain, output):
|
|||
|
||||
return providers
|
||||
|
||||
def _rust_test_impl(ctx):
|
||||
"""The implementation of the `rust_test` rule
|
||||
|
||||
Args:
|
||||
ctx (ctx): The rule's context object
|
||||
|
||||
Returns:
|
||||
list: A list of providers. See `_rust_test_common`
|
||||
"""
|
||||
toolchain = find_toolchain(ctx)
|
||||
|
||||
output = ctx.actions.declare_file(
|
||||
ctx.label.name + toolchain.binary_ext,
|
||||
)
|
||||
|
||||
return _rust_test_common(ctx, toolchain, output)
|
||||
|
||||
_common_attrs = {
|
||||
"aliases": attr.label_keyed_string_dict(
|
||||
doc = dedent("""\
|
||||
|
|
|
@ -7,7 +7,7 @@ rust_library(
|
|||
)
|
||||
|
||||
rust_test_suite(
|
||||
name = "integrated_tests_suite",
|
||||
name = "tests_suite",
|
||||
srcs = glob(["tests/**"]),
|
||||
edition = "2018",
|
||||
deps = [":math_lib"],
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
|
||||
load("//rust:defs.bzl", "rust_binary", "rust_common", "rust_library", "rust_test")
|
||||
load("//test/unit:common.bzl", "assert_action_mnemonic")
|
||||
|
||||
def _is_running_on_linux(ctx):
|
||||
|
@ -20,8 +20,13 @@ def _supports_linkstamps_test(ctx):
|
|||
linkstamp_out = linkstamp_action.outputs.to_list()[0]
|
||||
asserts.equals(env, linkstamp_out.basename, "linkstamp.o")
|
||||
tut_out = tut.files.to_list()[0]
|
||||
is_test = tut[rust_common.crate_info].is_test
|
||||
workspace_prefix = "" if ctx.workspace_name == "rules_rust" else "/external/rules_rust"
|
||||
expected_linkstamp_path = tut_out.dirname + "/_objs/" + tut_out.basename + workspace_prefix + "/test/unit/linkstamps/linkstamp.o"
|
||||
|
||||
# Rust compilation outputs coming from a test are put in test-{hash} directory
|
||||
# which we need to remove in order to obtain the linkstamp file path.
|
||||
dirname = "/".join(tut_out.dirname.split("/")[:-1]) if is_test else tut_out.dirname
|
||||
expected_linkstamp_path = dirname + "/_objs/" + tut_out.basename + workspace_prefix + "/test/unit/linkstamps/linkstamp.o"
|
||||
asserts.equals(
|
||||
env,
|
||||
linkstamp_out.path,
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
load(":rust_test_outputs.bzl", "rust_test_outputs_test_suite")
|
||||
|
||||
############################ UNIT TESTS #############################
|
||||
rust_test_outputs_test_suite(name = "rust_test_outputs_test_suite")
|
|
@ -0,0 +1 @@
|
|||
pub fn main() {}
|
|
@ -0,0 +1,64 @@
|
|||
"""Tests for rust_test outputs directory."""
|
||||
|
||||
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
|
||||
load("//rust:defs.bzl", "rust_binary", "rust_common", "rust_test")
|
||||
|
||||
def _rust_test_outputs_test(ctx):
|
||||
env = analysistest.begin(ctx)
|
||||
tut = analysistest.target_under_test(env)
|
||||
|
||||
output = tut[rust_common.crate_info].output
|
||||
|
||||
asserts.true(env, output.dirname.split("/")[-1].startswith("test-"))
|
||||
|
||||
return analysistest.end(env)
|
||||
|
||||
rust_test_outputs_test = analysistest.make(
|
||||
_rust_test_outputs_test,
|
||||
)
|
||||
|
||||
def _rust_test_outputs_targets():
|
||||
rust_binary(
|
||||
name = "bin_outputs",
|
||||
srcs = ["foo.rs"],
|
||||
edition = "2018",
|
||||
)
|
||||
|
||||
rust_test(
|
||||
name = "test_outputs_with_srcs",
|
||||
srcs = ["foo.rs"],
|
||||
edition = "2018",
|
||||
)
|
||||
|
||||
rust_test_outputs_test(
|
||||
name = "rust_test_outputs_using_srcs_attr",
|
||||
target_under_test = ":test_outputs_with_srcs",
|
||||
)
|
||||
|
||||
rust_test(
|
||||
name = "test_outputs_with_crate",
|
||||
crate = "bin_outputs",
|
||||
edition = "2018",
|
||||
)
|
||||
|
||||
rust_test_outputs_test(
|
||||
name = "rust_test_outputs_using_crate_attr",
|
||||
target_under_test = ":test_outputs_with_crate",
|
||||
)
|
||||
|
||||
def rust_test_outputs_test_suite(name):
|
||||
"""Entry-point macro called from the BUILD file.
|
||||
|
||||
Args:
|
||||
name: Name of the macro.
|
||||
"""
|
||||
|
||||
_rust_test_outputs_targets()
|
||||
|
||||
native.test_suite(
|
||||
name = name,
|
||||
tests = [
|
||||
":rust_test_outputs_using_srcs_attr",
|
||||
":rust_test_outputs_using_crate_attr",
|
||||
],
|
||||
)
|
Loading…
Reference in New Issue