blob: db69b6769099ef7800dc8aaf8b1a1a91a15d4b12 [file] [log] [blame]
# Copyright 2018 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load("@io_bazel_rules_rust//rust:private/rustc.bzl", "CrateInfo", "DepInfo", "get_lib_name")
load("@io_bazel_rules_rust//rust:private/utils.bzl", "find_toolchain")
def _rust_doc_test_impl(ctx):
if CrateInfo not in ctx.attr.dep:
fail("Expected rust library or binary.", "dep")
crate = ctx.attr.dep[CrateInfo]
rust_doc_test = ctx.outputs.executable
toolchain = find_toolchain(ctx)
dep_info = ctx.attr.dep[DepInfo]
# Construct rustdoc test command, which will be written to a shell script
# to be executed to run the test.
ctx.actions.write(
output = rust_doc_test,
content = _build_rustdoc_test_script(toolchain, dep_info, crate),
is_executable = True,
)
# The test script compiles the crate and runs it, so it needs both compile and runtime inputs.
compile_inputs = depset(
crate.srcs +
[crate.output] +
dep_info.transitive_libs +
[toolchain.rust_doc] +
[toolchain.rustc] +
toolchain.crosstool_files,
transitive = [
toolchain.rustc_lib.files,
toolchain.rust_lib.files,
],
)
runfiles = ctx.runfiles(
files = compile_inputs.to_list(),
collect_data = True,
)
return struct(runfiles = runfiles)
def dirname(path_str):
return "/".join(path_str.split("/")[:-1])
def _build_rustdoc_test_script(toolchain, dep_info, crate):
""" Constructs the rustdoc script used to test `crate`. """
d = dep_info
# nb. Paths must be constructed wrt runfiles, so we construct relative link flags for doctest.
link_flags = []
link_search_flags = []
link_flags += ["--extern=" + crate.name + "=" + crate.output.short_path]
link_flags += ["--extern=" + c.name + "=" + c.output.short_path for c in d.direct_crates.to_list()]
link_search_flags += ["-Ldependency={}".format(dirname(c.output.short_path)) for c in d.transitive_crates.to_list()]
link_flags += ["-ldylib=" + get_lib_name(lib) for lib in d.transitive_dylibs.to_list()]
link_search_flags += ["-Lnative={}".format(dirname(lib.short_path)) for lib in d.transitive_dylibs.to_list()]
link_flags += ["-lstatic=" + get_lib_name(lib) for lib in d.transitive_staticlibs.to_list()]
link_search_flags += ["-Lnative={}".format(dirname(lib.short_path)) for lib in d.transitive_staticlibs.to_list()]
edition_flags = ["--edition={}".format(crate.edition)] if crate.edition != "2015" else []
flags = link_search_flags + link_flags + edition_flags
return """\
#!/usr/bin/env bash
set -e;
{rust_doc} --test \\
{crate_root} \\
--crate-name={crate_name} \\
{flags}
""".format(
rust_doc = toolchain.rust_doc.path,
crate_root = crate.root.path,
crate_name = crate.name,
# TODO: Should be possible to do this with ctx.actions.Args, but can't seem to get them as a str and into the template.
flags = " \\\n ".join(flags),
)
rust_doc_test = rule(
_rust_doc_test_impl,
attrs = {
"dep": attr.label(
doc = """
The label of the target to run documentation tests for.
`rust_doc_test` can run documentation tests for the source files of
`rust_library` or `rust_binary` targets.
""",
mandatory = True,
providers = [CrateInfo],
),
},
executable = True,
test = True,
toolchains = ["@io_bazel_rules_rust//rust:toolchain"],
doc = """
Runs Rust documentation tests.
Example:
Suppose you have the following directory structure for a Rust library crate:
```
[workspace]/
WORKSPACE
hello_lib/
BUILD
src/
lib.rs
```
To run [documentation tests][doc-test] for the `hello_lib` crate, define a
`rust_doc_test` target that depends on the `hello_lib` `rust_library` target:
[doc-test]: https://doc.rust-lang.org/book/documentation.html#documentation-as-tests
```python
package(default_visibility = ["//visibility:public"])
load("@io_bazel_rules_rust//rust:rust.bzl", "rust_library", "rust_doc_test")
rust_library(
name = "hello_lib",
srcs = ["src/lib.rs"],
)
rust_doc_test(
name = "hello_lib_doc_test",
dep = ":hello_lib",
)
```
Running `bazel test //hello_lib:hello_lib_doc_test` will run all documentation tests for the `hello_lib` library crate.
""",
)