Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions rust/private/rustdoc_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,32 @@ def _compiled_rust_doc_test_impl(ctx, toolchain, crate_info):
is_executable = True,
)

# An rlib's runfiles need not include its native shared libraries. The
# persisted doctest executables still need those libraries at test time.
runtime_libraries = []
for target in [ctx.attr.crate] + ctx.attr.deps:
if CcInfo not in target:
continue
for linker_input in target[CcInfo].linking_context.linker_inputs.to_list():
for lib in linker_input.libraries:
if lib.static_library or lib.pic_static_library:
continue
if lib.dynamic_library:
runtime_libraries.append(lib.dynamic_library)
if lib.resolved_symlink_dynamic_library:
runtime_libraries.append(lib.resolved_symlink_dynamic_library)

return [
DefaultInfo(
files = depset([test_runner]),
runfiles = ctx.runfiles(
files = [doctest_dir, test_metadata, ctx.executable._test_runner_bin],
transitive_files = action.inputs,
),
files = [doctest_dir, test_metadata, ctx.executable._test_runner_bin] + runtime_libraries,
transitive_files = crate_info.compile_data,
).merge_all([
target[DefaultInfo].default_runfiles
for target in [ctx.attr.crate] + ctx.attr.deps + [ctx.attr._test_runner_bin]
if target[DefaultInfo].default_runfiles != None
]),
executable = test_runner,
),
RunEnvironmentInfo(
Expand Down
97 changes: 97 additions & 0 deletions test/rustdoc_runfiles/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
load("@rules_cc//cc:cc_shared_library.bzl", "cc_shared_library")
load("@rules_cc//cc:defs.bzl", "cc_import", "cc_library")
load("//rust:defs.bzl", "rust_doc_test", "rust_library")
load(":runfiles_test.bzl", "compiled_runfiles_test", "without_runfiles")

# Compiled doctest launchers do not yet keep their executable suffix on Windows.
NOT_WINDOWS = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
})

rust_doc_test(
name = "runtime_data_test",
crate = ":lib",
rustdoc_flags = ["--test-args=--nocapture"],
target_compatible_with = NOT_WINDOWS + select({
":compiled_doctests": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = [
":extra",
":native_without_runfiles",
],
)

compiled_runfiles_test(
name = "compiled_runfiles_test",
target_compatible_with = NOT_WINDOWS + select({
":compiled_doctests": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
target_under_test = ":runtime_data_test",
)

rust_library(
name = "lib",
srcs = ["lib.rs"],
compile_data = ["compile.txt"],
crate_name = "doctest_runfiles",
data = ["crate.txt"],
edition = "2021",
link_deps = [":native_import"],
target_compatible_with = NOT_WINDOWS,
deps = [":transitive"],
)

rust_library(
name = "transitive",
srcs = ["empty.rs"],
data = ["transitive.txt"],
edition = "2021",
)

rust_library(
name = "extra",
srcs = ["empty.rs"],
data = ["extra.txt"],
edition = "2021",
)

cc_import(
name = "native_import",
shared_library = ":native_shared",
target_compatible_with = NOT_WINDOWS,
)

without_runfiles(
name = "native_without_runfiles",
dep = ":native_import",
)

cc_shared_library(
name = "native_shared",
target_compatible_with = NOT_WINDOWS,
# Use the declared runtime path so this tests runfiles propagation without
# depending on rustdoc's executable-relative rpaths for persisted tests.
user_link_flags = select({
"@platforms//os:linux": ["-Wl,-soname,test/rustdoc_runfiles/libnative_shared.so"],
"@platforms//os:macos": ["-Wl,-install_name,test/rustdoc_runfiles/libnative_shared.dylib"],
"//conditions:default": [],
}),
deps = [":native"],
)

cc_library(
name = "native",
srcs = ["native.c"],
target_compatible_with = NOT_WINDOWS,
)

config_setting(
name = "compiled_doctests",
flag_values = {
"//rust/settings:experimental_compile_rustdoc_tests": "true",
"//rust/toolchain/channel:channel": "nightly",
},
)
1 change: 1 addition & 0 deletions test/rustdoc_runfiles/compile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
compile data
1 change: 1 addition & 0 deletions test/rustdoc_runfiles/crate.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
crate data
1 change: 1 addition & 0 deletions test/rustdoc_runfiles/empty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//! A dependency with declared runtime data.
1 change: 1 addition & 0 deletions test/rustdoc_runfiles/extra.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extra dependency data
28 changes: 28 additions & 0 deletions test/rustdoc_runfiles/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! Read declared runtime files and call a shared library from a persisted doctest.
//!
//! ```
//! use std::path::PathBuf;
//!
//! let root = PathBuf::from(std::env::var("TEST_SRCDIR")?)
//! .join(std::env::var("TEST_WORKSPACE")?)
//! .join("test/rustdoc_runfiles");
//! assert_eq!(std::fs::read_to_string(root.join("crate.txt"))?, "crate data\n");
//! assert_eq!(std::fs::read_to_string(root.join("transitive.txt"))?, "transitive data\n");
//! assert_eq!(std::fs::read_to_string(root.join("extra.txt"))?, "extra dependency data\n");
//! assert_eq!(include_str!("compile.txt"), "compile data\n");
//! assert_eq!(doctest_runfiles::answer(), 42);
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```

#![warn(rust_2018_idioms)]

#[link(name = "native_shared")]
extern "C" {
fn native_answer() -> i32;
}

/// Return the value exported by the native runtime dependency.
pub fn answer() -> i32 {
// SAFETY: native_answer takes no pointers and always returns a valid i32.
unsafe { native_answer() }
}
1 change: 1 addition & 0 deletions test/rustdoc_runfiles/native.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int native_answer(void) { return 42; }
42 changes: 42 additions & 0 deletions test/rustdoc_runfiles/runfiles_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Compiled doctests need runtime dependencies, not their compilation tools."""

load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")

def _without_runfiles_impl(ctx):
return [ctx.attr.dep[CcInfo]]

# A custom CcInfo dependency need not provide its own runtime runfiles.
without_runfiles = rule(
implementation = _without_runfiles_impl,
attrs = {"dep": attr.label(providers = [CcInfo])},
)

def _compiled_runfiles_test_impl(ctx):
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)
action = [action for action in target.actions if action.mnemonic == "RustdocTestCompile"][0]
inputs = action.inputs.to_list()
runfiles = target[DefaultInfo].default_runfiles.files.to_list()
paths = [file.short_path for file in runfiles]

for name in ["crate.txt", "transitive.txt", "extra.txt", "compile.txt"]:
asserts.true(env, "test/rustdoc_runfiles/" + name in paths, "Missing runfile: " + name)

compilers = [file for file in inputs if file.basename in ["rustc", "rustdoc", "rustc.exe", "rustdoc.exe"]]
asserts.true(env, bool(compilers), "Compilation must retain its compiler inputs")
for file in compilers:
asserts.false(env, file in runfiles, "Compiler should not be a test input: " + file.path)
for file in runfiles:
asserts.false(env, "/rust_toolchain/" in file.path, "Compiler SDK should not be a test input: " + file.path)

libraries = [file for file in inputs if "native_shared" in file.basename and file.extension in ["so", "dylib", "dll"]]
asserts.true(env, bool(libraries), "Fixture must link a shared native library")
for file in libraries:
asserts.true(env, file in runfiles, "Missing shared library: " + file.path)

return analysistest.end(env)

compiled_runfiles_test = analysistest.make(
_compiled_runfiles_test_impl,
)
1 change: 1 addition & 0 deletions test/rustdoc_runfiles/transitive.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
transitive data
Loading