| # 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. |
| |
| # buildifier: disable=module-docstring |
| load("//rust/private:common.bzl", "rust_common") |
| load("//rust/private:rustc.bzl", "add_crate_link_flags", "add_edition_flags") |
| load("//rust/private:utils.bzl", "find_toolchain") |
| |
| _rust_doc_doc = """Generates code documentation. |
| |
| Example: |
| Suppose you have the following directory structure for a Rust library crate: |
| |
| ``` |
| [workspace]/ |
| WORKSPACE |
| hello_lib/ |
| BUILD |
| src/ |
| lib.rs |
| ``` |
| |
| To build [`rustdoc`][rustdoc] documentation for the `hello_lib` crate, define \ |
| a `rust_doc` rule that depends on the the `hello_lib` `rust_library` target: |
| |
| [rustdoc]: https://doc.rust-lang.org/book/documentation.html |
| |
| ```python |
| package(default_visibility = ["//visibility:public"]) |
| |
| load("@rules_rust//rust:rust.bzl", "rust_library", "rust_doc") |
| |
| rust_library( |
| name = "hello_lib", |
| srcs = ["src/lib.rs"], |
| ) |
| |
| rust_doc( |
| name = "hello_lib_doc", |
| crate = ":hello_lib", |
| ) |
| ``` |
| |
| Running `bazel build //hello_lib:hello_lib_doc` will build a zip file containing \ |
| the documentation for the `hello_lib` library crate generated by `rustdoc`. |
| """ |
| |
| def _rust_doc_impl(ctx): |
| """The implementation of the `rust_doc` rule |
| |
| Args: |
| ctx (ctx): The rule's context object |
| """ |
| |
| if ctx.attr.crate and ctx.attr.dep: |
| fail("{} should only use the `crate` attribute. `dep` is deprecated".format( |
| ctx.label, |
| )) |
| |
| crate = ctx.attr.crate or ctx.attr.dep |
| if not crate: |
| fail("{} is missing the `crate` attribute".format(ctx.label)) |
| |
| crate_info = crate[rust_common.crate_info] |
| dep_info = crate[rust_common.dep_info] |
| |
| toolchain = find_toolchain(ctx) |
| |
| rustdoc_inputs = depset( |
| [c.output for c in dep_info.transitive_crates.to_list()] + |
| [toolchain.rust_doc], |
| transitive = [ |
| crate_info.srcs, |
| toolchain.rustc_lib.files, |
| toolchain.rust_lib.files, |
| ], |
| ) |
| |
| output_dir = ctx.actions.declare_directory(ctx.label.name) |
| args = ctx.actions.args() |
| args.add(crate_info.root.path) |
| args.add("--crate-name", crate_info.name) |
| args.add("--crate-type", crate_info.type) |
| if crate_info.type == "proc-macro": |
| args.add("--extern") |
| args.add("proc_macro") |
| args.add("--output", output_dir.path) |
| add_edition_flags(args, crate_info) |
| |
| # nb. rustdoc can't do anything with native link flags; we must omit them. |
| add_crate_link_flags(args, dep_info) |
| |
| args.add_all(ctx.files.markdown_css, before_each = "--markdown-css") |
| if ctx.file.html_in_header: |
| args.add("--html-in-header", ctx.file.html_in_header) |
| if ctx.file.html_before_content: |
| args.add("--html-before-content", ctx.file.html_before_content) |
| if ctx.file.html_after_content: |
| args.add("--html-after-content", ctx.file.html_after_content) |
| |
| ctx.actions.run( |
| executable = toolchain.rust_doc, |
| inputs = rustdoc_inputs, |
| outputs = [output_dir], |
| arguments = [args], |
| mnemonic = "Rustdoc", |
| progress_message = "Generating rustdoc for {} ({} files)".format( |
| crate_info.name, |
| len(crate_info.srcs.to_list()), |
| ), |
| ) |
| |
| # This rule does nothing without a single-file output, though the directory should've sufficed. |
| _zip_action(ctx, output_dir, ctx.outputs.rust_doc_zip) |
| |
| def _zip_action(ctx, input_dir, output_zip): |
| """Creates an archive of the generated documentation from `rustdoc` |
| |
| Args: |
| ctx (ctx): The `rust_doc` rule's context object |
| input_dir (File): A directory containing the outputs from rustdoc |
| output_zip (File): The location of the output archive containing generated documentation |
| """ |
| args = ctx.actions.args() |
| args.add(ctx.executable._zipper) |
| args.add(output_zip) |
| args.add(ctx.bin_dir.path) |
| args.add_all([input_dir], expand_directories = True) |
| ctx.actions.run( |
| executable = ctx.executable._dir_zipper, |
| inputs = [input_dir], |
| outputs = [output_zip], |
| arguments = [args], |
| tools = [ctx.executable._zipper], |
| ) |
| |
| rust_doc = rule( |
| doc = _rust_doc_doc, |
| implementation = _rust_doc_impl, |
| attrs = { |
| "crate": attr.label( |
| doc = ( |
| "The label of the target to generate code documentation for.\n" + |
| "\n" + |
| "`rust_doc` can generate HTML code documentation for the source files of " + |
| "`rust_library` or `rust_binary` targets." |
| ), |
| providers = [rust_common.crate_info], |
| # TODO: Make this attribute mandatory once `dep` is removed |
| ), |
| "dep": attr.label( |
| doc = "__deprecated__: use `crate`", |
| providers = [rust_common.crate_info], |
| ), |
| "html_after_content": attr.label( |
| doc = "File to add in `<body>`, after content.", |
| allow_single_file = [".html", ".md"], |
| ), |
| "html_before_content": attr.label( |
| doc = "File to add in `<body>`, before content.", |
| allow_single_file = [".html", ".md"], |
| ), |
| "html_in_header": attr.label( |
| doc = "File to add to `<head>`.", |
| allow_single_file = [".html", ".md"], |
| ), |
| "markdown_css": attr.label_list( |
| doc = "CSS files to include via `<link>` in a rendered Markdown file.", |
| allow_files = [".css"], |
| ), |
| "_dir_zipper": attr.label( |
| default = Label("//util/dir_zipper"), |
| cfg = "exec", |
| executable = True, |
| ), |
| "_zipper": attr.label( |
| default = Label("@bazel_tools//tools/zip:zipper"), |
| cfg = "exec", |
| executable = True, |
| ), |
| }, |
| outputs = { |
| "rust_doc_zip": "%{name}.zip", |
| }, |
| toolchains = [str(Label("//rust:toolchain"))], |
| incompatible_use_toolchain_transition = True, |
| ) |