xref: /aosp_15_r20/external/bazelbuild-rules_rust/rust/private/rustdoc.bzl (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1*d4726bddSHONG Yifan# Copyright 2018 The Bazel Authors. All rights reserved.
2*d4726bddSHONG Yifan#
3*d4726bddSHONG Yifan# Licensed under the Apache License, Version 2.0 (the "License");
4*d4726bddSHONG Yifan# you may not use this file except in compliance with the License.
5*d4726bddSHONG Yifan# You may obtain a copy of the License at
6*d4726bddSHONG Yifan#
7*d4726bddSHONG Yifan#    http://www.apache.org/licenses/LICENSE-2.0
8*d4726bddSHONG Yifan#
9*d4726bddSHONG Yifan# Unless required by applicable law or agreed to in writing, software
10*d4726bddSHONG Yifan# distributed under the License is distributed on an "AS IS" BASIS,
11*d4726bddSHONG Yifan# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*d4726bddSHONG Yifan# See the License for the specific language governing permissions and
13*d4726bddSHONG Yifan# limitations under the License.
14*d4726bddSHONG Yifan
15*d4726bddSHONG Yifan"""Rules for generating documentation with `rustdoc` for Bazel built crates"""
16*d4726bddSHONG Yifan
17*d4726bddSHONG Yifanload("//rust/private:common.bzl", "rust_common")
18*d4726bddSHONG Yifanload("//rust/private:rustc.bzl", "collect_deps", "collect_inputs", "construct_arguments")
19*d4726bddSHONG Yifanload("//rust/private:utils.bzl", "dedent", "find_cc_toolchain", "find_toolchain")
20*d4726bddSHONG Yifan
21*d4726bddSHONG Yifandef _strip_crate_info_output(crate_info):
22*d4726bddSHONG Yifan    """Set the CrateInfo.output to None for a given CrateInfo provider.
23*d4726bddSHONG Yifan
24*d4726bddSHONG Yifan    Args:
25*d4726bddSHONG Yifan        crate_info (CrateInfo): A provider
26*d4726bddSHONG Yifan
27*d4726bddSHONG Yifan    Returns:
28*d4726bddSHONG Yifan        CrateInfo: A modified CrateInfo provider
29*d4726bddSHONG Yifan    """
30*d4726bddSHONG Yifan    return rust_common.create_crate_info(
31*d4726bddSHONG Yifan        name = crate_info.name,
32*d4726bddSHONG Yifan        type = crate_info.type,
33*d4726bddSHONG Yifan        root = crate_info.root,
34*d4726bddSHONG Yifan        srcs = crate_info.srcs,
35*d4726bddSHONG Yifan        deps = crate_info.deps,
36*d4726bddSHONG Yifan        proc_macro_deps = crate_info.proc_macro_deps,
37*d4726bddSHONG Yifan        aliases = crate_info.aliases,
38*d4726bddSHONG Yifan        # This crate info should have no output
39*d4726bddSHONG Yifan        output = None,
40*d4726bddSHONG Yifan        metadata = None,
41*d4726bddSHONG Yifan        edition = crate_info.edition,
42*d4726bddSHONG Yifan        rustc_env = crate_info.rustc_env,
43*d4726bddSHONG Yifan        rustc_env_files = crate_info.rustc_env_files,
44*d4726bddSHONG Yifan        is_test = crate_info.is_test,
45*d4726bddSHONG Yifan        compile_data = crate_info.compile_data,
46*d4726bddSHONG Yifan        compile_data_targets = crate_info.compile_data_targets,
47*d4726bddSHONG Yifan        data = crate_info.data,
48*d4726bddSHONG Yifan    )
49*d4726bddSHONG Yifan
50*d4726bddSHONG Yifandef rustdoc_compile_action(
51*d4726bddSHONG Yifan        ctx,
52*d4726bddSHONG Yifan        toolchain,
53*d4726bddSHONG Yifan        crate_info,
54*d4726bddSHONG Yifan        output = None,
55*d4726bddSHONG Yifan        rustdoc_flags = [],
56*d4726bddSHONG Yifan        is_test = False):
57*d4726bddSHONG Yifan    """Create a struct of information needed for a `rustdoc` compile action based on crate passed to the rustdoc rule.
58*d4726bddSHONG Yifan
59*d4726bddSHONG Yifan    Args:
60*d4726bddSHONG Yifan        ctx (ctx): The rule's context object.
61*d4726bddSHONG Yifan        toolchain (rust_toolchain): The currently configured `rust_toolchain`.
62*d4726bddSHONG Yifan        crate_info (CrateInfo): The provider of the crate passed to a rustdoc rule.
63*d4726bddSHONG Yifan        output (File, optional): An optional output a `rustdoc` action is intended to produce.
64*d4726bddSHONG Yifan        rustdoc_flags (list, optional): A list of `rustdoc` specific flags.
65*d4726bddSHONG Yifan        is_test (bool, optional): If True, the action will be configured for `rust_doc_test` targets
66*d4726bddSHONG Yifan
67*d4726bddSHONG Yifan    Returns:
68*d4726bddSHONG Yifan        struct: A struct of some `ctx.actions.run` arguments.
69*d4726bddSHONG Yifan    """
70*d4726bddSHONG Yifan
71*d4726bddSHONG Yifan    # If an output was provided, ensure it's used in rustdoc arguments
72*d4726bddSHONG Yifan    if output:
73*d4726bddSHONG Yifan        rustdoc_flags = [
74*d4726bddSHONG Yifan            "--output",
75*d4726bddSHONG Yifan            output.path,
76*d4726bddSHONG Yifan        ] + rustdoc_flags
77*d4726bddSHONG Yifan
78*d4726bddSHONG Yifan    cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
79*d4726bddSHONG Yifan
80*d4726bddSHONG Yifan    dep_info, build_info, _ = collect_deps(
81*d4726bddSHONG Yifan        deps = crate_info.deps,
82*d4726bddSHONG Yifan        proc_macro_deps = crate_info.proc_macro_deps,
83*d4726bddSHONG Yifan        aliases = crate_info.aliases,
84*d4726bddSHONG Yifan    )
85*d4726bddSHONG Yifan
86*d4726bddSHONG Yifan    compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs(
87*d4726bddSHONG Yifan        ctx = ctx,
88*d4726bddSHONG Yifan        file = ctx.file,
89*d4726bddSHONG Yifan        files = ctx.files,
90*d4726bddSHONG Yifan        linkstamps = depset([]),
91*d4726bddSHONG Yifan        toolchain = toolchain,
92*d4726bddSHONG Yifan        cc_toolchain = cc_toolchain,
93*d4726bddSHONG Yifan        feature_configuration = feature_configuration,
94*d4726bddSHONG Yifan        crate_info = crate_info,
95*d4726bddSHONG Yifan        dep_info = dep_info,
96*d4726bddSHONG Yifan        build_info = build_info,
97*d4726bddSHONG Yifan        # If this is a rustdoc test, we need to depend on rlibs rather than .rmeta.
98*d4726bddSHONG Yifan        force_depend_on_objects = is_test,
99*d4726bddSHONG Yifan        include_link_flags = False,
100*d4726bddSHONG Yifan    )
101*d4726bddSHONG Yifan
102*d4726bddSHONG Yifan    # Since this crate is not actually producing the output described by the
103*d4726bddSHONG Yifan    # given CrateInfo, this attribute needs to be stripped to allow the rest
104*d4726bddSHONG Yifan    # of the rustc functionality in `construct_arguments` to avoid generating
105*d4726bddSHONG Yifan    # arguments expecting to do so.
106*d4726bddSHONG Yifan    rustdoc_crate_info = _strip_crate_info_output(crate_info)
107*d4726bddSHONG Yifan
108*d4726bddSHONG Yifan    args, env = construct_arguments(
109*d4726bddSHONG Yifan        ctx = ctx,
110*d4726bddSHONG Yifan        attr = ctx.attr,
111*d4726bddSHONG Yifan        file = ctx.file,
112*d4726bddSHONG Yifan        toolchain = toolchain,
113*d4726bddSHONG Yifan        tool_path = toolchain.rust_doc.short_path if is_test else toolchain.rust_doc.path,
114*d4726bddSHONG Yifan        cc_toolchain = cc_toolchain,
115*d4726bddSHONG Yifan        feature_configuration = feature_configuration,
116*d4726bddSHONG Yifan        crate_info = rustdoc_crate_info,
117*d4726bddSHONG Yifan        dep_info = dep_info,
118*d4726bddSHONG Yifan        linkstamp_outs = linkstamp_outs,
119*d4726bddSHONG Yifan        ambiguous_libs = ambiguous_libs,
120*d4726bddSHONG Yifan        output_hash = None,
121*d4726bddSHONG Yifan        rust_flags = rustdoc_flags,
122*d4726bddSHONG Yifan        out_dir = out_dir,
123*d4726bddSHONG Yifan        build_env_files = build_env_files,
124*d4726bddSHONG Yifan        build_flags_files = build_flags_files,
125*d4726bddSHONG Yifan        emit = [],
126*d4726bddSHONG Yifan        remap_path_prefix = None,
127*d4726bddSHONG Yifan        add_flags_for_binary = True,
128*d4726bddSHONG Yifan        include_link_flags = False,
129*d4726bddSHONG Yifan        force_depend_on_objects = is_test,
130*d4726bddSHONG Yifan        skip_expanding_rustc_env = True,
131*d4726bddSHONG Yifan    )
132*d4726bddSHONG Yifan
133*d4726bddSHONG Yifan    # Because rustdoc tests compile tests outside of the sandbox, the sysroot
134*d4726bddSHONG Yifan    # must be updated to the `short_path` equivilant as it will now be
135*d4726bddSHONG Yifan    # a part of runfiles.
136*d4726bddSHONG Yifan    if is_test:
137*d4726bddSHONG Yifan        if "SYSROOT" in env:
138*d4726bddSHONG Yifan            env.update({"SYSROOT": "${{pwd}}/{}".format(toolchain.sysroot_short_path)})
139*d4726bddSHONG Yifan        if "OUT_DIR" in env:
140*d4726bddSHONG Yifan            env.update({"OUT_DIR": "${{pwd}}/{}".format(build_info.out_dir.short_path)})
141*d4726bddSHONG Yifan
142*d4726bddSHONG Yifan    return struct(
143*d4726bddSHONG Yifan        executable = ctx.executable._process_wrapper,
144*d4726bddSHONG Yifan        inputs = depset([crate_info.output], transitive = [compile_inputs]),
145*d4726bddSHONG Yifan        env = env,
146*d4726bddSHONG Yifan        arguments = args.all,
147*d4726bddSHONG Yifan        tools = [toolchain.rust_doc],
148*d4726bddSHONG Yifan    )
149*d4726bddSHONG Yifan
150*d4726bddSHONG Yifandef _zip_action(ctx, input_dir, output_zip, crate_label):
151*d4726bddSHONG Yifan    """Creates an archive of the generated documentation from `rustdoc`
152*d4726bddSHONG Yifan
153*d4726bddSHONG Yifan    Args:
154*d4726bddSHONG Yifan        ctx (ctx): The `rust_doc` rule's context object
155*d4726bddSHONG Yifan        input_dir (File): A directory containing the outputs from rustdoc
156*d4726bddSHONG Yifan        output_zip (File): The location of the output archive containing generated documentation
157*d4726bddSHONG Yifan        crate_label (Label): The label of the crate docs are being generated for.
158*d4726bddSHONG Yifan    """
159*d4726bddSHONG Yifan    args = ctx.actions.args()
160*d4726bddSHONG Yifan    args.add(ctx.executable._zipper)
161*d4726bddSHONG Yifan    args.add(output_zip)
162*d4726bddSHONG Yifan    args.add(ctx.bin_dir.path)
163*d4726bddSHONG Yifan    args.add_all([input_dir], expand_directories = True)
164*d4726bddSHONG Yifan    ctx.actions.run(
165*d4726bddSHONG Yifan        executable = ctx.executable._dir_zipper,
166*d4726bddSHONG Yifan        inputs = [input_dir],
167*d4726bddSHONG Yifan        outputs = [output_zip],
168*d4726bddSHONG Yifan        arguments = [args],
169*d4726bddSHONG Yifan        mnemonic = "RustdocZip",
170*d4726bddSHONG Yifan        progress_message = "Creating RustdocZip for {}".format(crate_label),
171*d4726bddSHONG Yifan        tools = [ctx.executable._zipper],
172*d4726bddSHONG Yifan    )
173*d4726bddSHONG Yifan
174*d4726bddSHONG Yifandef _rust_doc_impl(ctx):
175*d4726bddSHONG Yifan    """The implementation of the `rust_doc` rule
176*d4726bddSHONG Yifan
177*d4726bddSHONG Yifan    Args:
178*d4726bddSHONG Yifan        ctx (ctx): The rule's context object
179*d4726bddSHONG Yifan    """
180*d4726bddSHONG Yifan
181*d4726bddSHONG Yifan    if ctx.attr.rustc_flags:
182*d4726bddSHONG Yifan        # buildifier: disable=print
183*d4726bddSHONG Yifan        print("rustc_flags is deprecated in favor of `rustdoc_flags` for rustdoc targets. Please update {}".format(
184*d4726bddSHONG Yifan            ctx.label,
185*d4726bddSHONG Yifan        ))
186*d4726bddSHONG Yifan
187*d4726bddSHONG Yifan    crate = ctx.attr.crate
188*d4726bddSHONG Yifan    crate_info = crate[rust_common.crate_info]
189*d4726bddSHONG Yifan
190*d4726bddSHONG Yifan    output_dir = ctx.actions.declare_directory("{}.rustdoc".format(ctx.label.name))
191*d4726bddSHONG Yifan
192*d4726bddSHONG Yifan    # Add the current crate as an extern for the compile action
193*d4726bddSHONG Yifan    rustdoc_flags = [
194*d4726bddSHONG Yifan        "--extern",
195*d4726bddSHONG Yifan        "{}={}".format(crate_info.name, crate_info.output.path),
196*d4726bddSHONG Yifan    ]
197*d4726bddSHONG Yifan
198*d4726bddSHONG Yifan    rustdoc_flags.extend(ctx.attr.rustdoc_flags)
199*d4726bddSHONG Yifan
200*d4726bddSHONG Yifan    action = rustdoc_compile_action(
201*d4726bddSHONG Yifan        ctx = ctx,
202*d4726bddSHONG Yifan        toolchain = find_toolchain(ctx),
203*d4726bddSHONG Yifan        crate_info = crate_info,
204*d4726bddSHONG Yifan        output = output_dir,
205*d4726bddSHONG Yifan        rustdoc_flags = rustdoc_flags,
206*d4726bddSHONG Yifan    )
207*d4726bddSHONG Yifan
208*d4726bddSHONG Yifan    ctx.actions.run(
209*d4726bddSHONG Yifan        mnemonic = "Rustdoc",
210*d4726bddSHONG Yifan        progress_message = "Generating Rustdoc for {}".format(crate.label),
211*d4726bddSHONG Yifan        outputs = [output_dir],
212*d4726bddSHONG Yifan        executable = action.executable,
213*d4726bddSHONG Yifan        inputs = action.inputs,
214*d4726bddSHONG Yifan        env = action.env,
215*d4726bddSHONG Yifan        arguments = action.arguments,
216*d4726bddSHONG Yifan        tools = action.tools,
217*d4726bddSHONG Yifan    )
218*d4726bddSHONG Yifan
219*d4726bddSHONG Yifan    # This rule does nothing without a single-file output, though the directory should've sufficed.
220*d4726bddSHONG Yifan    _zip_action(ctx, output_dir, ctx.outputs.rust_doc_zip, crate.label)
221*d4726bddSHONG Yifan
222*d4726bddSHONG Yifan    return [
223*d4726bddSHONG Yifan        DefaultInfo(
224*d4726bddSHONG Yifan            files = depset([output_dir]),
225*d4726bddSHONG Yifan        ),
226*d4726bddSHONG Yifan        OutputGroupInfo(
227*d4726bddSHONG Yifan            rustdoc_dir = depset([output_dir]),
228*d4726bddSHONG Yifan            rustdoc_zip = depset([ctx.outputs.rust_doc_zip]),
229*d4726bddSHONG Yifan        ),
230*d4726bddSHONG Yifan    ]
231*d4726bddSHONG Yifan
232*d4726bddSHONG Yifanrust_doc = rule(
233*d4726bddSHONG Yifan    doc = dedent("""\
234*d4726bddSHONG Yifan    Generates code documentation.
235*d4726bddSHONG Yifan
236*d4726bddSHONG Yifan    Example:
237*d4726bddSHONG Yifan    Suppose you have the following directory structure for a Rust library crate:
238*d4726bddSHONG Yifan
239*d4726bddSHONG Yifan    ```
240*d4726bddSHONG Yifan    [workspace]/
241*d4726bddSHONG Yifan        WORKSPACE
242*d4726bddSHONG Yifan        hello_lib/
243*d4726bddSHONG Yifan            BUILD
244*d4726bddSHONG Yifan            src/
245*d4726bddSHONG Yifan                lib.rs
246*d4726bddSHONG Yifan    ```
247*d4726bddSHONG Yifan
248*d4726bddSHONG Yifan    To build [`rustdoc`][rustdoc] documentation for the `hello_lib` crate, define \
249*d4726bddSHONG Yifan    a `rust_doc` rule that depends on the the `hello_lib` `rust_library` target:
250*d4726bddSHONG Yifan
251*d4726bddSHONG Yifan    [rustdoc]: https://doc.rust-lang.org/book/documentation.html
252*d4726bddSHONG Yifan
253*d4726bddSHONG Yifan    ```python
254*d4726bddSHONG Yifan    package(default_visibility = ["//visibility:public"])
255*d4726bddSHONG Yifan
256*d4726bddSHONG Yifan    load("@rules_rust//rust:defs.bzl", "rust_library", "rust_doc")
257*d4726bddSHONG Yifan
258*d4726bddSHONG Yifan    rust_library(
259*d4726bddSHONG Yifan        name = "hello_lib",
260*d4726bddSHONG Yifan        srcs = ["src/lib.rs"],
261*d4726bddSHONG Yifan    )
262*d4726bddSHONG Yifan
263*d4726bddSHONG Yifan    rust_doc(
264*d4726bddSHONG Yifan        name = "hello_lib_doc",
265*d4726bddSHONG Yifan        crate = ":hello_lib",
266*d4726bddSHONG Yifan    )
267*d4726bddSHONG Yifan    ```
268*d4726bddSHONG Yifan
269*d4726bddSHONG Yifan    Running `bazel build //hello_lib:hello_lib_doc` will build a zip file containing \
270*d4726bddSHONG Yifan    the documentation for the `hello_lib` library crate generated by `rustdoc`.
271*d4726bddSHONG Yifan    """),
272*d4726bddSHONG Yifan    implementation = _rust_doc_impl,
273*d4726bddSHONG Yifan    attrs = {
274*d4726bddSHONG Yifan        "crate": attr.label(
275*d4726bddSHONG Yifan            doc = (
276*d4726bddSHONG Yifan                "The label of the target to generate code documentation for.\n" +
277*d4726bddSHONG Yifan                "\n" +
278*d4726bddSHONG Yifan                "`rust_doc` can generate HTML code documentation for the source files of " +
279*d4726bddSHONG Yifan                "`rust_library` or `rust_binary` targets."
280*d4726bddSHONG Yifan            ),
281*d4726bddSHONG Yifan            providers = [rust_common.crate_info],
282*d4726bddSHONG Yifan            mandatory = True,
283*d4726bddSHONG Yifan        ),
284*d4726bddSHONG Yifan        "html_after_content": attr.label(
285*d4726bddSHONG Yifan            doc = "File to add in `<body>`, after content.",
286*d4726bddSHONG Yifan            allow_single_file = [".html", ".md"],
287*d4726bddSHONG Yifan        ),
288*d4726bddSHONG Yifan        "html_before_content": attr.label(
289*d4726bddSHONG Yifan            doc = "File to add in `<body>`, before content.",
290*d4726bddSHONG Yifan            allow_single_file = [".html", ".md"],
291*d4726bddSHONG Yifan        ),
292*d4726bddSHONG Yifan        "html_in_header": attr.label(
293*d4726bddSHONG Yifan            doc = "File to add to `<head>`.",
294*d4726bddSHONG Yifan            allow_single_file = [".html", ".md"],
295*d4726bddSHONG Yifan        ),
296*d4726bddSHONG Yifan        "markdown_css": attr.label_list(
297*d4726bddSHONG Yifan            doc = "CSS files to include via `<link>` in a rendered Markdown file.",
298*d4726bddSHONG Yifan            allow_files = [".css"],
299*d4726bddSHONG Yifan        ),
300*d4726bddSHONG Yifan        "rustc_flags": attr.string_list(
301*d4726bddSHONG Yifan            doc = "**Deprecated**: use `rustdoc_flags` instead",
302*d4726bddSHONG Yifan        ),
303*d4726bddSHONG Yifan        "rustdoc_flags": attr.string_list(
304*d4726bddSHONG Yifan            doc = dedent("""\
305*d4726bddSHONG Yifan                List of flags passed to `rustdoc`.
306*d4726bddSHONG Yifan
307*d4726bddSHONG Yifan                These strings are subject to Make variable expansion for predefined
308*d4726bddSHONG Yifan                source/output path variables like `$location`, `$execpath`, and
309*d4726bddSHONG Yifan                `$rootpath`. This expansion is useful if you wish to pass a generated
310*d4726bddSHONG Yifan                file of arguments to rustc: `@$(location //package:target)`.
311*d4726bddSHONG Yifan            """),
312*d4726bddSHONG Yifan        ),
313*d4726bddSHONG Yifan        "_cc_toolchain": attr.label(
314*d4726bddSHONG Yifan            doc = "In order to use find_cpp_toolchain, you must define the '_cc_toolchain' attribute on your rule or aspect.",
315*d4726bddSHONG Yifan            default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
316*d4726bddSHONG Yifan        ),
317*d4726bddSHONG Yifan        "_dir_zipper": attr.label(
318*d4726bddSHONG Yifan            doc = "A tool that orchestrates the creation of zip archives for rustdoc outputs.",
319*d4726bddSHONG Yifan            default = Label("//util/dir_zipper"),
320*d4726bddSHONG Yifan            cfg = "exec",
321*d4726bddSHONG Yifan            executable = True,
322*d4726bddSHONG Yifan        ),
323*d4726bddSHONG Yifan        "_error_format": attr.label(
324*d4726bddSHONG Yifan            default = Label("//:error_format"),
325*d4726bddSHONG Yifan        ),
326*d4726bddSHONG Yifan        "_process_wrapper": attr.label(
327*d4726bddSHONG Yifan            doc = "A process wrapper for running rustdoc on all platforms",
328*d4726bddSHONG Yifan            default = Label("@rules_rust//util/process_wrapper"),
329*d4726bddSHONG Yifan            executable = True,
330*d4726bddSHONG Yifan            allow_single_file = True,
331*d4726bddSHONG Yifan            cfg = "exec",
332*d4726bddSHONG Yifan        ),
333*d4726bddSHONG Yifan        "_zipper": attr.label(
334*d4726bddSHONG Yifan            doc = "A Bazel provided tool for creating archives",
335*d4726bddSHONG Yifan            default = Label("@bazel_tools//tools/zip:zipper"),
336*d4726bddSHONG Yifan            cfg = "exec",
337*d4726bddSHONG Yifan            executable = True,
338*d4726bddSHONG Yifan        ),
339*d4726bddSHONG Yifan    },
340*d4726bddSHONG Yifan    fragments = ["cpp"],
341*d4726bddSHONG Yifan    outputs = {
342*d4726bddSHONG Yifan        "rust_doc_zip": "%{name}.zip",
343*d4726bddSHONG Yifan    },
344*d4726bddSHONG Yifan    toolchains = [
345*d4726bddSHONG Yifan        str(Label("//rust:toolchain_type")),
346*d4726bddSHONG Yifan        "@bazel_tools//tools/cpp:toolchain_type",
347*d4726bddSHONG Yifan    ],
348*d4726bddSHONG Yifan)
349