1*60517a1eSAndroid Build Coastguard Worker# Copyright 2023 The Bazel Authors. All rights reserved. 2*60517a1eSAndroid Build Coastguard Worker# 3*60517a1eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*60517a1eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*60517a1eSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*60517a1eSAndroid Build Coastguard Worker# 7*60517a1eSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*60517a1eSAndroid Build Coastguard Worker# 9*60517a1eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*60517a1eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*60517a1eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*60517a1eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*60517a1eSAndroid Build Coastguard Worker# limitations under the License. 14*60517a1eSAndroid Build Coastguard Worker 15*60517a1eSAndroid Build Coastguard Worker"""Implementation of sphinx rules.""" 16*60517a1eSAndroid Build Coastguard Worker 17*60517a1eSAndroid Build Coastguard Workerload("@bazel_skylib//lib:paths.bzl", "paths") 18*60517a1eSAndroid Build Coastguard Workerload("@bazel_skylib//rules:build_test.bzl", "build_test") 19*60517a1eSAndroid Build Coastguard Workerload("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") 20*60517a1eSAndroid Build Coastguard Workerload("//python:py_binary.bzl", "py_binary") 21*60517a1eSAndroid Build Coastguard Workerload("//python/private:util.bzl", "add_tag", "copy_propagating_kwargs") # buildifier: disable=bzl-visibility 22*60517a1eSAndroid Build Coastguard Workerload(":sphinx_docs_library_info.bzl", "SphinxDocsLibraryInfo") 23*60517a1eSAndroid Build Coastguard Worker 24*60517a1eSAndroid Build Coastguard Worker_SPHINX_BUILD_MAIN_SRC = Label("//sphinxdocs/private:sphinx_build.py") 25*60517a1eSAndroid Build Coastguard Worker_SPHINX_SERVE_MAIN_SRC = Label("//sphinxdocs/private:sphinx_server.py") 26*60517a1eSAndroid Build Coastguard Worker 27*60517a1eSAndroid Build Coastguard Worker_SphinxSourceTreeInfo = provider( 28*60517a1eSAndroid Build Coastguard Worker doc = "Information about source tree for Sphinx to build.", 29*60517a1eSAndroid Build Coastguard Worker fields = { 30*60517a1eSAndroid Build Coastguard Worker "source_dir_runfiles_path": """ 31*60517a1eSAndroid Build Coastguard Worker:type: str 32*60517a1eSAndroid Build Coastguard Worker 33*60517a1eSAndroid Build Coastguard WorkerRunfiles-root relative path of the root directory for the source files. 34*60517a1eSAndroid Build Coastguard Worker""", 35*60517a1eSAndroid Build Coastguard Worker "source_root": """ 36*60517a1eSAndroid Build Coastguard Worker:type: str 37*60517a1eSAndroid Build Coastguard Worker 38*60517a1eSAndroid Build Coastguard WorkerExec-root relative path of the root directory for the source files (which are in DefaultInfo.files) 39*60517a1eSAndroid Build Coastguard Worker""", 40*60517a1eSAndroid Build Coastguard Worker }, 41*60517a1eSAndroid Build Coastguard Worker) 42*60517a1eSAndroid Build Coastguard Worker 43*60517a1eSAndroid Build Coastguard Worker_SphinxRunInfo = provider( 44*60517a1eSAndroid Build Coastguard Worker doc = "Information for running the underlying Sphinx command directly", 45*60517a1eSAndroid Build Coastguard Worker fields = { 46*60517a1eSAndroid Build Coastguard Worker "per_format_args": """ 47*60517a1eSAndroid Build Coastguard Worker:type: dict[str, struct] 48*60517a1eSAndroid Build Coastguard Worker 49*60517a1eSAndroid Build Coastguard WorkerA dict keyed by output format name. The values are a struct with attributes: 50*60517a1eSAndroid Build Coastguard Worker* args: a `list[str]` of args to run this format's build 51*60517a1eSAndroid Build Coastguard Worker* env: a `dict[str, str]` of environment variables to set for this format's build 52*60517a1eSAndroid Build Coastguard Worker""", 53*60517a1eSAndroid Build Coastguard Worker "source_tree": """ 54*60517a1eSAndroid Build Coastguard Worker:type: Target 55*60517a1eSAndroid Build Coastguard Worker 56*60517a1eSAndroid Build Coastguard WorkerTarget with the source tree files 57*60517a1eSAndroid Build Coastguard Worker""", 58*60517a1eSAndroid Build Coastguard Worker "sphinx": """ 59*60517a1eSAndroid Build Coastguard Worker:type: Target 60*60517a1eSAndroid Build Coastguard Worker 61*60517a1eSAndroid Build Coastguard WorkerThe sphinx-build binary to run. 62*60517a1eSAndroid Build Coastguard Worker""", 63*60517a1eSAndroid Build Coastguard Worker "tools": """ 64*60517a1eSAndroid Build Coastguard Worker:type: list[Target] 65*60517a1eSAndroid Build Coastguard Worker 66*60517a1eSAndroid Build Coastguard WorkerAdditional tools Sphinx needs 67*60517a1eSAndroid Build Coastguard Worker""", 68*60517a1eSAndroid Build Coastguard Worker }, 69*60517a1eSAndroid Build Coastguard Worker) 70*60517a1eSAndroid Build Coastguard Worker 71*60517a1eSAndroid Build Coastguard Workerdef sphinx_build_binary(name, py_binary_rule = py_binary, **kwargs): 72*60517a1eSAndroid Build Coastguard Worker """Create an executable with the sphinx-build command line interface. 73*60517a1eSAndroid Build Coastguard Worker 74*60517a1eSAndroid Build Coastguard Worker The `deps` must contain the sphinx library and any other extensions Sphinx 75*60517a1eSAndroid Build Coastguard Worker needs at runtime. 76*60517a1eSAndroid Build Coastguard Worker 77*60517a1eSAndroid Build Coastguard Worker Args: 78*60517a1eSAndroid Build Coastguard Worker name: {type}`str` name of the target. The name "sphinx-build" is the 79*60517a1eSAndroid Build Coastguard Worker conventional name to match what Sphinx itself uses. 80*60517a1eSAndroid Build Coastguard Worker py_binary_rule: {type}`callable` A `py_binary` compatible callable 81*60517a1eSAndroid Build Coastguard Worker for creating the target. If not set, the regular `py_binary` 82*60517a1eSAndroid Build Coastguard Worker rule is used. This allows using the version-aware rules, or 83*60517a1eSAndroid Build Coastguard Worker other alternative implementations. 84*60517a1eSAndroid Build Coastguard Worker **kwargs: {type}`dict` Additional kwargs to pass onto `py_binary`. The `srcs` and 85*60517a1eSAndroid Build Coastguard Worker `main` attributes must not be specified. 86*60517a1eSAndroid Build Coastguard Worker """ 87*60517a1eSAndroid Build Coastguard Worker add_tag(kwargs, "@rules_python//sphinxdocs:sphinx_build_binary") 88*60517a1eSAndroid Build Coastguard Worker py_binary_rule( 89*60517a1eSAndroid Build Coastguard Worker name = name, 90*60517a1eSAndroid Build Coastguard Worker srcs = [_SPHINX_BUILD_MAIN_SRC], 91*60517a1eSAndroid Build Coastguard Worker main = _SPHINX_BUILD_MAIN_SRC, 92*60517a1eSAndroid Build Coastguard Worker **kwargs 93*60517a1eSAndroid Build Coastguard Worker ) 94*60517a1eSAndroid Build Coastguard Worker 95*60517a1eSAndroid Build Coastguard Workerdef sphinx_docs( 96*60517a1eSAndroid Build Coastguard Worker name, 97*60517a1eSAndroid Build Coastguard Worker *, 98*60517a1eSAndroid Build Coastguard Worker srcs = [], 99*60517a1eSAndroid Build Coastguard Worker deps = [], 100*60517a1eSAndroid Build Coastguard Worker renamed_srcs = {}, 101*60517a1eSAndroid Build Coastguard Worker sphinx, 102*60517a1eSAndroid Build Coastguard Worker config, 103*60517a1eSAndroid Build Coastguard Worker formats, 104*60517a1eSAndroid Build Coastguard Worker strip_prefix = "", 105*60517a1eSAndroid Build Coastguard Worker extra_opts = [], 106*60517a1eSAndroid Build Coastguard Worker tools = [], 107*60517a1eSAndroid Build Coastguard Worker **kwargs): 108*60517a1eSAndroid Build Coastguard Worker """Generate docs using Sphinx. 109*60517a1eSAndroid Build Coastguard Worker 110*60517a1eSAndroid Build Coastguard Worker Generates targets: 111*60517a1eSAndroid Build Coastguard Worker * `<name>`: The output of this target is a directory for each 112*60517a1eSAndroid Build Coastguard Worker format Sphinx creates. This target also has a separate output 113*60517a1eSAndroid Build Coastguard Worker group for each format. e.g. `--output_group=html` will only build 114*60517a1eSAndroid Build Coastguard Worker the "html" format files. 115*60517a1eSAndroid Build Coastguard Worker * `<name>.serve`: A binary that locally serves the HTML output. This 116*60517a1eSAndroid Build Coastguard Worker allows previewing docs during development. 117*60517a1eSAndroid Build Coastguard Worker * `<name>.run`: A binary that directly runs the underlying Sphinx command 118*60517a1eSAndroid Build Coastguard Worker to build the docs. This is a debugging aid. 119*60517a1eSAndroid Build Coastguard Worker 120*60517a1eSAndroid Build Coastguard Worker Args: 121*60517a1eSAndroid Build Coastguard Worker name: {type}`Name` name of the docs rule. 122*60517a1eSAndroid Build Coastguard Worker srcs: {type}`list[label]` The source files for Sphinx to process. 123*60517a1eSAndroid Build Coastguard Worker deps: {type}`list[label]` of {obj}`sphinx_docs_library` targets. 124*60517a1eSAndroid Build Coastguard Worker renamed_srcs: {type}`dict[label, dict]` Doc source files for Sphinx that 125*60517a1eSAndroid Build Coastguard Worker are renamed. This is typically used for files elsewhere, such as top 126*60517a1eSAndroid Build Coastguard Worker level files in the repo. 127*60517a1eSAndroid Build Coastguard Worker sphinx: {type}`label` the Sphinx tool to use for building 128*60517a1eSAndroid Build Coastguard Worker documentation. Because Sphinx supports various plugins, you must 129*60517a1eSAndroid Build Coastguard Worker construct your own binary with the necessary dependencies. The 130*60517a1eSAndroid Build Coastguard Worker {obj}`sphinx_build_binary` rule can be used to define such a binary, but 131*60517a1eSAndroid Build Coastguard Worker any executable supporting the `sphinx-build` command line interface 132*60517a1eSAndroid Build Coastguard Worker can be used (typically some `py_binary` program). 133*60517a1eSAndroid Build Coastguard Worker config: {type}`label` the Sphinx config file (`conf.py`) to use. 134*60517a1eSAndroid Build Coastguard Worker formats: (list of str) the formats (`-b` flag) to generate documentation 135*60517a1eSAndroid Build Coastguard Worker in. Each format will become an output group. 136*60517a1eSAndroid Build Coastguard Worker strip_prefix: {type}`str` A prefix to remove from the file paths of the 137*60517a1eSAndroid Build Coastguard Worker source files. e.g., given `//docs:foo.md`, stripping `docs/` makes 138*60517a1eSAndroid Build Coastguard Worker Sphinx see `foo.md` in its generated source directory. If not 139*60517a1eSAndroid Build Coastguard Worker specified, then {any}`native.package_name` is used. 140*60517a1eSAndroid Build Coastguard Worker extra_opts: {type}`list[str]` Additional options to pass onto Sphinx building. 141*60517a1eSAndroid Build Coastguard Worker On each provided option, a location expansion is performed. 142*60517a1eSAndroid Build Coastguard Worker See {any}`ctx.expand_location`. 143*60517a1eSAndroid Build Coastguard Worker tools: {type}`list[label]` Additional tools that are used by Sphinx and its plugins. 144*60517a1eSAndroid Build Coastguard Worker This just makes the tools available during Sphinx execution. To locate 145*60517a1eSAndroid Build Coastguard Worker them, use {obj}`extra_opts` and `$(location)`. 146*60517a1eSAndroid Build Coastguard Worker **kwargs: {type}`dict` Common attributes to pass onto rules. 147*60517a1eSAndroid Build Coastguard Worker """ 148*60517a1eSAndroid Build Coastguard Worker add_tag(kwargs, "@rules_python//sphinxdocs:sphinx_docs") 149*60517a1eSAndroid Build Coastguard Worker common_kwargs = copy_propagating_kwargs(kwargs) 150*60517a1eSAndroid Build Coastguard Worker 151*60517a1eSAndroid Build Coastguard Worker internal_name = "_{}".format(name.lstrip("_")) 152*60517a1eSAndroid Build Coastguard Worker 153*60517a1eSAndroid Build Coastguard Worker _sphinx_source_tree( 154*60517a1eSAndroid Build Coastguard Worker name = internal_name + "/_sources", 155*60517a1eSAndroid Build Coastguard Worker srcs = srcs, 156*60517a1eSAndroid Build Coastguard Worker deps = deps, 157*60517a1eSAndroid Build Coastguard Worker renamed_srcs = renamed_srcs, 158*60517a1eSAndroid Build Coastguard Worker config = config, 159*60517a1eSAndroid Build Coastguard Worker strip_prefix = strip_prefix, 160*60517a1eSAndroid Build Coastguard Worker **common_kwargs 161*60517a1eSAndroid Build Coastguard Worker ) 162*60517a1eSAndroid Build Coastguard Worker _sphinx_docs( 163*60517a1eSAndroid Build Coastguard Worker name = name, 164*60517a1eSAndroid Build Coastguard Worker sphinx = sphinx, 165*60517a1eSAndroid Build Coastguard Worker formats = formats, 166*60517a1eSAndroid Build Coastguard Worker source_tree = internal_name + "/_sources", 167*60517a1eSAndroid Build Coastguard Worker extra_opts = extra_opts, 168*60517a1eSAndroid Build Coastguard Worker tools = tools, 169*60517a1eSAndroid Build Coastguard Worker **kwargs 170*60517a1eSAndroid Build Coastguard Worker ) 171*60517a1eSAndroid Build Coastguard Worker 172*60517a1eSAndroid Build Coastguard Worker html_name = internal_name + "_html" 173*60517a1eSAndroid Build Coastguard Worker native.filegroup( 174*60517a1eSAndroid Build Coastguard Worker name = html_name, 175*60517a1eSAndroid Build Coastguard Worker srcs = [name], 176*60517a1eSAndroid Build Coastguard Worker output_group = "html", 177*60517a1eSAndroid Build Coastguard Worker **common_kwargs 178*60517a1eSAndroid Build Coastguard Worker ) 179*60517a1eSAndroid Build Coastguard Worker 180*60517a1eSAndroid Build Coastguard Worker py_binary( 181*60517a1eSAndroid Build Coastguard Worker name = name + ".serve", 182*60517a1eSAndroid Build Coastguard Worker srcs = [_SPHINX_SERVE_MAIN_SRC], 183*60517a1eSAndroid Build Coastguard Worker main = _SPHINX_SERVE_MAIN_SRC, 184*60517a1eSAndroid Build Coastguard Worker data = [html_name], 185*60517a1eSAndroid Build Coastguard Worker args = [ 186*60517a1eSAndroid Build Coastguard Worker "$(execpath {})".format(html_name), 187*60517a1eSAndroid Build Coastguard Worker ], 188*60517a1eSAndroid Build Coastguard Worker **common_kwargs 189*60517a1eSAndroid Build Coastguard Worker ) 190*60517a1eSAndroid Build Coastguard Worker sphinx_run( 191*60517a1eSAndroid Build Coastguard Worker name = name + ".run", 192*60517a1eSAndroid Build Coastguard Worker docs = name, 193*60517a1eSAndroid Build Coastguard Worker ) 194*60517a1eSAndroid Build Coastguard Worker 195*60517a1eSAndroid Build Coastguard Worker build_test( 196*60517a1eSAndroid Build Coastguard Worker name = name + "_build_test", 197*60517a1eSAndroid Build Coastguard Worker targets = [name], 198*60517a1eSAndroid Build Coastguard Worker **kwargs # kwargs used to pick up target_compatible_with 199*60517a1eSAndroid Build Coastguard Worker ) 200*60517a1eSAndroid Build Coastguard Worker 201*60517a1eSAndroid Build Coastguard Workerdef _sphinx_docs_impl(ctx): 202*60517a1eSAndroid Build Coastguard Worker source_tree_info = ctx.attr.source_tree[_SphinxSourceTreeInfo] 203*60517a1eSAndroid Build Coastguard Worker source_dir_path = source_tree_info.source_root 204*60517a1eSAndroid Build Coastguard Worker inputs = ctx.attr.source_tree[DefaultInfo].files 205*60517a1eSAndroid Build Coastguard Worker 206*60517a1eSAndroid Build Coastguard Worker per_format_args = {} 207*60517a1eSAndroid Build Coastguard Worker outputs = {} 208*60517a1eSAndroid Build Coastguard Worker for format in ctx.attr.formats: 209*60517a1eSAndroid Build Coastguard Worker output_dir, args_env = _run_sphinx( 210*60517a1eSAndroid Build Coastguard Worker ctx = ctx, 211*60517a1eSAndroid Build Coastguard Worker format = format, 212*60517a1eSAndroid Build Coastguard Worker source_path = source_dir_path, 213*60517a1eSAndroid Build Coastguard Worker output_prefix = paths.join(ctx.label.name, "_build"), 214*60517a1eSAndroid Build Coastguard Worker inputs = inputs, 215*60517a1eSAndroid Build Coastguard Worker ) 216*60517a1eSAndroid Build Coastguard Worker outputs[format] = output_dir 217*60517a1eSAndroid Build Coastguard Worker per_format_args[format] = args_env 218*60517a1eSAndroid Build Coastguard Worker return [ 219*60517a1eSAndroid Build Coastguard Worker DefaultInfo(files = depset(outputs.values())), 220*60517a1eSAndroid Build Coastguard Worker OutputGroupInfo(**{ 221*60517a1eSAndroid Build Coastguard Worker format: depset([output]) 222*60517a1eSAndroid Build Coastguard Worker for format, output in outputs.items() 223*60517a1eSAndroid Build Coastguard Worker }), 224*60517a1eSAndroid Build Coastguard Worker _SphinxRunInfo( 225*60517a1eSAndroid Build Coastguard Worker sphinx = ctx.attr.sphinx, 226*60517a1eSAndroid Build Coastguard Worker source_tree = ctx.attr.source_tree, 227*60517a1eSAndroid Build Coastguard Worker tools = ctx.attr.tools, 228*60517a1eSAndroid Build Coastguard Worker per_format_args = per_format_args, 229*60517a1eSAndroid Build Coastguard Worker ), 230*60517a1eSAndroid Build Coastguard Worker ] 231*60517a1eSAndroid Build Coastguard Worker 232*60517a1eSAndroid Build Coastguard Worker_sphinx_docs = rule( 233*60517a1eSAndroid Build Coastguard Worker implementation = _sphinx_docs_impl, 234*60517a1eSAndroid Build Coastguard Worker attrs = { 235*60517a1eSAndroid Build Coastguard Worker "extra_opts": attr.string_list( 236*60517a1eSAndroid Build Coastguard Worker doc = "Additional options to pass onto Sphinx. These are added after " + 237*60517a1eSAndroid Build Coastguard Worker "other options, but before the source/output args.", 238*60517a1eSAndroid Build Coastguard Worker ), 239*60517a1eSAndroid Build Coastguard Worker "formats": attr.string_list(doc = "Output formats for Sphinx to create."), 240*60517a1eSAndroid Build Coastguard Worker "source_tree": attr.label( 241*60517a1eSAndroid Build Coastguard Worker doc = "Directory of files for Sphinx to process.", 242*60517a1eSAndroid Build Coastguard Worker providers = [_SphinxSourceTreeInfo], 243*60517a1eSAndroid Build Coastguard Worker ), 244*60517a1eSAndroid Build Coastguard Worker "sphinx": attr.label( 245*60517a1eSAndroid Build Coastguard Worker executable = True, 246*60517a1eSAndroid Build Coastguard Worker cfg = "exec", 247*60517a1eSAndroid Build Coastguard Worker mandatory = True, 248*60517a1eSAndroid Build Coastguard Worker doc = "Sphinx binary to generate documentation.", 249*60517a1eSAndroid Build Coastguard Worker ), 250*60517a1eSAndroid Build Coastguard Worker "tools": attr.label_list( 251*60517a1eSAndroid Build Coastguard Worker cfg = "exec", 252*60517a1eSAndroid Build Coastguard Worker doc = "Additional tools that are used by Sphinx and its plugins.", 253*60517a1eSAndroid Build Coastguard Worker ), 254*60517a1eSAndroid Build Coastguard Worker "_extra_defines_flag": attr.label(default = "//sphinxdocs:extra_defines"), 255*60517a1eSAndroid Build Coastguard Worker "_extra_env_flag": attr.label(default = "//sphinxdocs:extra_env"), 256*60517a1eSAndroid Build Coastguard Worker "_quiet_flag": attr.label(default = "//sphinxdocs:quiet"), 257*60517a1eSAndroid Build Coastguard Worker }, 258*60517a1eSAndroid Build Coastguard Worker) 259*60517a1eSAndroid Build Coastguard Worker 260*60517a1eSAndroid Build Coastguard Workerdef _run_sphinx(ctx, format, source_path, inputs, output_prefix): 261*60517a1eSAndroid Build Coastguard Worker output_dir = ctx.actions.declare_directory(paths.join(output_prefix, format)) 262*60517a1eSAndroid Build Coastguard Worker 263*60517a1eSAndroid Build Coastguard Worker run_args = [] # Copy of the args to forward along to debug runner 264*60517a1eSAndroid Build Coastguard Worker args = ctx.actions.args() # Args passed to the action 265*60517a1eSAndroid Build Coastguard Worker 266*60517a1eSAndroid Build Coastguard Worker args.add("--show-traceback") # Full tracebacks on error 267*60517a1eSAndroid Build Coastguard Worker run_args.append("--show-traceback") 268*60517a1eSAndroid Build Coastguard Worker args.add("--builder", format) 269*60517a1eSAndroid Build Coastguard Worker run_args.extend(("--builder", format)) 270*60517a1eSAndroid Build Coastguard Worker 271*60517a1eSAndroid Build Coastguard Worker if ctx.attr._quiet_flag[BuildSettingInfo].value: 272*60517a1eSAndroid Build Coastguard Worker # Not added to run_args because run_args is for debugging 273*60517a1eSAndroid Build Coastguard Worker args.add("--quiet") # Suppress stdout informational text 274*60517a1eSAndroid Build Coastguard Worker 275*60517a1eSAndroid Build Coastguard Worker # Build in parallel, if possible 276*60517a1eSAndroid Build Coastguard Worker # Don't add to run_args: parallel building breaks interactive debugging 277*60517a1eSAndroid Build Coastguard Worker args.add("--jobs", "auto") 278*60517a1eSAndroid Build Coastguard Worker args.add("--fresh-env") # Don't try to use cache files. Bazel can't make use of them. 279*60517a1eSAndroid Build Coastguard Worker run_args.append("--fresh-env") 280*60517a1eSAndroid Build Coastguard Worker args.add("--write-all") # Write all files; don't try to detect "changed" files 281*60517a1eSAndroid Build Coastguard Worker run_args.append("--write-all") 282*60517a1eSAndroid Build Coastguard Worker 283*60517a1eSAndroid Build Coastguard Worker for opt in ctx.attr.extra_opts: 284*60517a1eSAndroid Build Coastguard Worker expanded = ctx.expand_location(opt) 285*60517a1eSAndroid Build Coastguard Worker args.add(expanded) 286*60517a1eSAndroid Build Coastguard Worker run_args.append(expanded) 287*60517a1eSAndroid Build Coastguard Worker 288*60517a1eSAndroid Build Coastguard Worker extra_defines = ctx.attr._extra_defines_flag[_FlagInfo].value 289*60517a1eSAndroid Build Coastguard Worker args.add_all(extra_defines, before_each = "--define") 290*60517a1eSAndroid Build Coastguard Worker for define in extra_defines: 291*60517a1eSAndroid Build Coastguard Worker run_args.extend(("--define", define)) 292*60517a1eSAndroid Build Coastguard Worker 293*60517a1eSAndroid Build Coastguard Worker args.add(source_path) 294*60517a1eSAndroid Build Coastguard Worker args.add(output_dir.path) 295*60517a1eSAndroid Build Coastguard Worker 296*60517a1eSAndroid Build Coastguard Worker env = dict([ 297*60517a1eSAndroid Build Coastguard Worker v.split("=", 1) 298*60517a1eSAndroid Build Coastguard Worker for v in ctx.attr._extra_env_flag[_FlagInfo].value 299*60517a1eSAndroid Build Coastguard Worker ]) 300*60517a1eSAndroid Build Coastguard Worker 301*60517a1eSAndroid Build Coastguard Worker tools = [] 302*60517a1eSAndroid Build Coastguard Worker for tool in ctx.attr.tools: 303*60517a1eSAndroid Build Coastguard Worker tools.append(tool[DefaultInfo].files_to_run) 304*60517a1eSAndroid Build Coastguard Worker 305*60517a1eSAndroid Build Coastguard Worker ctx.actions.run( 306*60517a1eSAndroid Build Coastguard Worker executable = ctx.executable.sphinx, 307*60517a1eSAndroid Build Coastguard Worker arguments = [args], 308*60517a1eSAndroid Build Coastguard Worker inputs = inputs, 309*60517a1eSAndroid Build Coastguard Worker outputs = [output_dir], 310*60517a1eSAndroid Build Coastguard Worker tools = tools, 311*60517a1eSAndroid Build Coastguard Worker mnemonic = "SphinxBuildDocs", 312*60517a1eSAndroid Build Coastguard Worker progress_message = "Sphinx building {} for %{{label}}".format(format), 313*60517a1eSAndroid Build Coastguard Worker env = env, 314*60517a1eSAndroid Build Coastguard Worker ) 315*60517a1eSAndroid Build Coastguard Worker return output_dir, struct(args = run_args, env = env) 316*60517a1eSAndroid Build Coastguard Worker 317*60517a1eSAndroid Build Coastguard Workerdef _sphinx_source_tree_impl(ctx): 318*60517a1eSAndroid Build Coastguard Worker # Sphinx only accepts a single directory to read its doc sources from. 319*60517a1eSAndroid Build Coastguard Worker # Because plain files and generated files are in different directories, 320*60517a1eSAndroid Build Coastguard Worker # we need to merge the two into a single directory. 321*60517a1eSAndroid Build Coastguard Worker source_prefix = ctx.label.name 322*60517a1eSAndroid Build Coastguard Worker sphinx_source_files = [] 323*60517a1eSAndroid Build Coastguard Worker 324*60517a1eSAndroid Build Coastguard Worker # Materialize a file under the `_sources` dir 325*60517a1eSAndroid Build Coastguard Worker def _relocate(source_file, dest_path = None): 326*60517a1eSAndroid Build Coastguard Worker if not dest_path: 327*60517a1eSAndroid Build Coastguard Worker dest_path = source_file.short_path.removeprefix(ctx.attr.strip_prefix) 328*60517a1eSAndroid Build Coastguard Worker dest_file = ctx.actions.declare_file(paths.join(source_prefix, dest_path)) 329*60517a1eSAndroid Build Coastguard Worker ctx.actions.symlink( 330*60517a1eSAndroid Build Coastguard Worker output = dest_file, 331*60517a1eSAndroid Build Coastguard Worker target_file = source_file, 332*60517a1eSAndroid Build Coastguard Worker progress_message = "Symlinking Sphinx source %{input} to %{output}", 333*60517a1eSAndroid Build Coastguard Worker ) 334*60517a1eSAndroid Build Coastguard Worker sphinx_source_files.append(dest_file) 335*60517a1eSAndroid Build Coastguard Worker return dest_file 336*60517a1eSAndroid Build Coastguard Worker 337*60517a1eSAndroid Build Coastguard Worker # Though Sphinx has a -c flag, we move the config file into the sources 338*60517a1eSAndroid Build Coastguard Worker # directory to make the config more intuitive because some configuration 339*60517a1eSAndroid Build Coastguard Worker # options are relative to the config location, not the sources directory. 340*60517a1eSAndroid Build Coastguard Worker source_conf_file = _relocate(ctx.file.config) 341*60517a1eSAndroid Build Coastguard Worker sphinx_source_dir_path = paths.dirname(source_conf_file.path) 342*60517a1eSAndroid Build Coastguard Worker 343*60517a1eSAndroid Build Coastguard Worker for src in ctx.attr.srcs: 344*60517a1eSAndroid Build Coastguard Worker if SphinxDocsLibraryInfo in src: 345*60517a1eSAndroid Build Coastguard Worker fail(( 346*60517a1eSAndroid Build Coastguard Worker "In attribute srcs: target {src} is misplaced here: " + 347*60517a1eSAndroid Build Coastguard Worker "sphinx_docs_library targets belong in the deps attribute." 348*60517a1eSAndroid Build Coastguard Worker ).format(src = src)) 349*60517a1eSAndroid Build Coastguard Worker 350*60517a1eSAndroid Build Coastguard Worker for orig_file in ctx.files.srcs: 351*60517a1eSAndroid Build Coastguard Worker _relocate(orig_file) 352*60517a1eSAndroid Build Coastguard Worker 353*60517a1eSAndroid Build Coastguard Worker for src_target, dest in ctx.attr.renamed_srcs.items(): 354*60517a1eSAndroid Build Coastguard Worker src_files = src_target.files.to_list() 355*60517a1eSAndroid Build Coastguard Worker if len(src_files) != 1: 356*60517a1eSAndroid Build Coastguard Worker fail("A single file must be specified to be renamed. Target {} " + 357*60517a1eSAndroid Build Coastguard Worker "generate {} files: {}".format( 358*60517a1eSAndroid Build Coastguard Worker src_target, 359*60517a1eSAndroid Build Coastguard Worker len(src_files), 360*60517a1eSAndroid Build Coastguard Worker src_files, 361*60517a1eSAndroid Build Coastguard Worker )) 362*60517a1eSAndroid Build Coastguard Worker _relocate(src_files[0], dest) 363*60517a1eSAndroid Build Coastguard Worker 364*60517a1eSAndroid Build Coastguard Worker for t in ctx.attr.deps: 365*60517a1eSAndroid Build Coastguard Worker info = t[SphinxDocsLibraryInfo] 366*60517a1eSAndroid Build Coastguard Worker for entry in info.transitive.to_list(): 367*60517a1eSAndroid Build Coastguard Worker for original in entry.files: 368*60517a1eSAndroid Build Coastguard Worker new_path = entry.prefix + original.short_path.removeprefix(entry.strip_prefix) 369*60517a1eSAndroid Build Coastguard Worker _relocate(original, new_path) 370*60517a1eSAndroid Build Coastguard Worker 371*60517a1eSAndroid Build Coastguard Worker return [ 372*60517a1eSAndroid Build Coastguard Worker DefaultInfo( 373*60517a1eSAndroid Build Coastguard Worker files = depset(sphinx_source_files), 374*60517a1eSAndroid Build Coastguard Worker ), 375*60517a1eSAndroid Build Coastguard Worker _SphinxSourceTreeInfo( 376*60517a1eSAndroid Build Coastguard Worker source_root = sphinx_source_dir_path, 377*60517a1eSAndroid Build Coastguard Worker source_dir_runfiles_path = paths.dirname(source_conf_file.short_path), 378*60517a1eSAndroid Build Coastguard Worker ), 379*60517a1eSAndroid Build Coastguard Worker ] 380*60517a1eSAndroid Build Coastguard Worker 381*60517a1eSAndroid Build Coastguard Worker_sphinx_source_tree = rule( 382*60517a1eSAndroid Build Coastguard Worker implementation = _sphinx_source_tree_impl, 383*60517a1eSAndroid Build Coastguard Worker attrs = { 384*60517a1eSAndroid Build Coastguard Worker "config": attr.label( 385*60517a1eSAndroid Build Coastguard Worker allow_single_file = True, 386*60517a1eSAndroid Build Coastguard Worker mandatory = True, 387*60517a1eSAndroid Build Coastguard Worker doc = "Config file for Sphinx", 388*60517a1eSAndroid Build Coastguard Worker ), 389*60517a1eSAndroid Build Coastguard Worker "deps": attr.label_list( 390*60517a1eSAndroid Build Coastguard Worker providers = [SphinxDocsLibraryInfo], 391*60517a1eSAndroid Build Coastguard Worker ), 392*60517a1eSAndroid Build Coastguard Worker "renamed_srcs": attr.label_keyed_string_dict( 393*60517a1eSAndroid Build Coastguard Worker allow_files = True, 394*60517a1eSAndroid Build Coastguard Worker doc = "Doc source files for Sphinx that are renamed. This is " + 395*60517a1eSAndroid Build Coastguard Worker "typically used for files elsewhere, such as top level " + 396*60517a1eSAndroid Build Coastguard Worker "files in the repo.", 397*60517a1eSAndroid Build Coastguard Worker ), 398*60517a1eSAndroid Build Coastguard Worker "srcs": attr.label_list( 399*60517a1eSAndroid Build Coastguard Worker allow_files = True, 400*60517a1eSAndroid Build Coastguard Worker doc = "Doc source files for Sphinx.", 401*60517a1eSAndroid Build Coastguard Worker ), 402*60517a1eSAndroid Build Coastguard Worker "strip_prefix": attr.string(doc = "Prefix to remove from input file paths."), 403*60517a1eSAndroid Build Coastguard Worker }, 404*60517a1eSAndroid Build Coastguard Worker) 405*60517a1eSAndroid Build Coastguard Worker_FlagInfo = provider( 406*60517a1eSAndroid Build Coastguard Worker doc = "Provider for a flag value", 407*60517a1eSAndroid Build Coastguard Worker fields = ["value"], 408*60517a1eSAndroid Build Coastguard Worker) 409*60517a1eSAndroid Build Coastguard Worker 410*60517a1eSAndroid Build Coastguard Workerdef _repeated_string_list_flag_impl(ctx): 411*60517a1eSAndroid Build Coastguard Worker return _FlagInfo(value = ctx.build_setting_value) 412*60517a1eSAndroid Build Coastguard Worker 413*60517a1eSAndroid Build Coastguard Workerrepeated_string_list_flag = rule( 414*60517a1eSAndroid Build Coastguard Worker implementation = _repeated_string_list_flag_impl, 415*60517a1eSAndroid Build Coastguard Worker build_setting = config.string_list(flag = True, repeatable = True), 416*60517a1eSAndroid Build Coastguard Worker) 417*60517a1eSAndroid Build Coastguard Worker 418*60517a1eSAndroid Build Coastguard Workerdef sphinx_inventory(*, name, src, **kwargs): 419*60517a1eSAndroid Build Coastguard Worker """Creates a compressed inventory file from an uncompressed on. 420*60517a1eSAndroid Build Coastguard Worker 421*60517a1eSAndroid Build Coastguard Worker The Sphinx inventory format isn't formally documented, but is understood 422*60517a1eSAndroid Build Coastguard Worker to be: 423*60517a1eSAndroid Build Coastguard Worker 424*60517a1eSAndroid Build Coastguard Worker ``` 425*60517a1eSAndroid Build Coastguard Worker # Sphinx inventory version 2 426*60517a1eSAndroid Build Coastguard Worker # Project: <project name> 427*60517a1eSAndroid Build Coastguard Worker # Version: <version string> 428*60517a1eSAndroid Build Coastguard Worker # The remainder of this file is compressed using zlib 429*60517a1eSAndroid Build Coastguard Worker name domain:role 1 relative-url display name 430*60517a1eSAndroid Build Coastguard Worker ``` 431*60517a1eSAndroid Build Coastguard Worker 432*60517a1eSAndroid Build Coastguard Worker Where: 433*60517a1eSAndroid Build Coastguard Worker * `<project name>` is a string. e.g. `Rules Python` 434*60517a1eSAndroid Build Coastguard Worker * `<version string>` is a string e.g. `1.5.3` 435*60517a1eSAndroid Build Coastguard Worker 436*60517a1eSAndroid Build Coastguard Worker And there are one or more `name domain:role ...` lines 437*60517a1eSAndroid Build Coastguard Worker * `name`: the name of the symbol. It can contain special characters, 438*60517a1eSAndroid Build Coastguard Worker but not spaces. 439*60517a1eSAndroid Build Coastguard Worker * `domain:role`: The `domain` is usually a language, e.g. `py` or `bzl`. 440*60517a1eSAndroid Build Coastguard Worker The `role` is usually the type of object, e.g. `class` or `func`. There 441*60517a1eSAndroid Build Coastguard Worker is no canonical meaning to the values, they are usually domain-specific. 442*60517a1eSAndroid Build Coastguard Worker * `1` is a number. It affects search priority. 443*60517a1eSAndroid Build Coastguard Worker * `relative-url` is a URL path relative to the base url in the 444*60517a1eSAndroid Build Coastguard Worker confg.py intersphinx config. 445*60517a1eSAndroid Build Coastguard Worker * `display name` is a string. It can contain spaces, or simply be 446*60517a1eSAndroid Build Coastguard Worker the value `-` to indicate it is the same as `name` 447*60517a1eSAndroid Build Coastguard Worker 448*60517a1eSAndroid Build Coastguard Worker :::{seealso} 449*60517a1eSAndroid Build Coastguard Worker {bzl:obj}`//sphinxdocs/inventories` for inventories of Bazel objects. 450*60517a1eSAndroid Build Coastguard Worker ::: 451*60517a1eSAndroid Build Coastguard Worker 452*60517a1eSAndroid Build Coastguard Worker Args: 453*60517a1eSAndroid Build Coastguard Worker name: {type}`Name` name of the target. 454*60517a1eSAndroid Build Coastguard Worker src: {type}`label` Uncompressed inventory text file. 455*60517a1eSAndroid Build Coastguard Worker **kwargs: {type}`dict` additional kwargs of common attributes. 456*60517a1eSAndroid Build Coastguard Worker """ 457*60517a1eSAndroid Build Coastguard Worker _sphinx_inventory(name = name, src = src, **kwargs) 458*60517a1eSAndroid Build Coastguard Worker 459*60517a1eSAndroid Build Coastguard Workerdef _sphinx_inventory_impl(ctx): 460*60517a1eSAndroid Build Coastguard Worker output = ctx.actions.declare_file(ctx.label.name + ".inv") 461*60517a1eSAndroid Build Coastguard Worker args = ctx.actions.args() 462*60517a1eSAndroid Build Coastguard Worker args.add(ctx.file.src) 463*60517a1eSAndroid Build Coastguard Worker args.add(output) 464*60517a1eSAndroid Build Coastguard Worker ctx.actions.run( 465*60517a1eSAndroid Build Coastguard Worker executable = ctx.executable._builder, 466*60517a1eSAndroid Build Coastguard Worker arguments = [args], 467*60517a1eSAndroid Build Coastguard Worker inputs = depset([ctx.file.src]), 468*60517a1eSAndroid Build Coastguard Worker outputs = [output], 469*60517a1eSAndroid Build Coastguard Worker ) 470*60517a1eSAndroid Build Coastguard Worker return [DefaultInfo(files = depset([output]))] 471*60517a1eSAndroid Build Coastguard Worker 472*60517a1eSAndroid Build Coastguard Worker_sphinx_inventory = rule( 473*60517a1eSAndroid Build Coastguard Worker implementation = _sphinx_inventory_impl, 474*60517a1eSAndroid Build Coastguard Worker attrs = { 475*60517a1eSAndroid Build Coastguard Worker "src": attr.label(allow_single_file = True), 476*60517a1eSAndroid Build Coastguard Worker "_builder": attr.label( 477*60517a1eSAndroid Build Coastguard Worker default = "//sphinxdocs/private:inventory_builder", 478*60517a1eSAndroid Build Coastguard Worker executable = True, 479*60517a1eSAndroid Build Coastguard Worker cfg = "exec", 480*60517a1eSAndroid Build Coastguard Worker ), 481*60517a1eSAndroid Build Coastguard Worker }, 482*60517a1eSAndroid Build Coastguard Worker) 483*60517a1eSAndroid Build Coastguard Worker 484*60517a1eSAndroid Build Coastguard Workerdef _sphinx_run_impl(ctx): 485*60517a1eSAndroid Build Coastguard Worker run_info = ctx.attr.docs[_SphinxRunInfo] 486*60517a1eSAndroid Build Coastguard Worker 487*60517a1eSAndroid Build Coastguard Worker builder = ctx.attr.builder 488*60517a1eSAndroid Build Coastguard Worker 489*60517a1eSAndroid Build Coastguard Worker if builder not in run_info.per_format_args: 490*60517a1eSAndroid Build Coastguard Worker builder = run_info.per_format_args.keys()[0] 491*60517a1eSAndroid Build Coastguard Worker 492*60517a1eSAndroid Build Coastguard Worker args_info = run_info.per_format_args.get(builder) 493*60517a1eSAndroid Build Coastguard Worker if not args_info: 494*60517a1eSAndroid Build Coastguard Worker fail("Format {} not built by {}".format( 495*60517a1eSAndroid Build Coastguard Worker builder, 496*60517a1eSAndroid Build Coastguard Worker ctx.attr.docs.label, 497*60517a1eSAndroid Build Coastguard Worker )) 498*60517a1eSAndroid Build Coastguard Worker 499*60517a1eSAndroid Build Coastguard Worker args_str = [] 500*60517a1eSAndroid Build Coastguard Worker args_str.extend(args_info.args) 501*60517a1eSAndroid Build Coastguard Worker args_str = "\n".join(["args+=('{}')".format(value) for value in args_info.args]) 502*60517a1eSAndroid Build Coastguard Worker if not args_str: 503*60517a1eSAndroid Build Coastguard Worker args_str = "# empty custom args" 504*60517a1eSAndroid Build Coastguard Worker 505*60517a1eSAndroid Build Coastguard Worker env_str = "\n".join([ 506*60517a1eSAndroid Build Coastguard Worker "sphinx_env+=({}='{}')".format(*item) 507*60517a1eSAndroid Build Coastguard Worker for item in args_info.env.items() 508*60517a1eSAndroid Build Coastguard Worker ]) 509*60517a1eSAndroid Build Coastguard Worker if not env_str: 510*60517a1eSAndroid Build Coastguard Worker env_str = "# empty custom env" 511*60517a1eSAndroid Build Coastguard Worker 512*60517a1eSAndroid Build Coastguard Worker executable = ctx.actions.declare_file(ctx.label.name) 513*60517a1eSAndroid Build Coastguard Worker sphinx = run_info.sphinx 514*60517a1eSAndroid Build Coastguard Worker ctx.actions.expand_template( 515*60517a1eSAndroid Build Coastguard Worker template = ctx.file._template, 516*60517a1eSAndroid Build Coastguard Worker output = executable, 517*60517a1eSAndroid Build Coastguard Worker substitutions = { 518*60517a1eSAndroid Build Coastguard Worker "%SETUP_ARGS%": args_str, 519*60517a1eSAndroid Build Coastguard Worker "%SETUP_ENV%": env_str, 520*60517a1eSAndroid Build Coastguard Worker "%SOURCE_DIR_EXEC_PATH%": run_info.source_tree[_SphinxSourceTreeInfo].source_root, 521*60517a1eSAndroid Build Coastguard Worker "%SOURCE_DIR_RUNFILES_PATH%": run_info.source_tree[_SphinxSourceTreeInfo].source_dir_runfiles_path, 522*60517a1eSAndroid Build Coastguard Worker "%SPHINX_EXEC_PATH%": sphinx[DefaultInfo].files_to_run.executable.path, 523*60517a1eSAndroid Build Coastguard Worker "%SPHINX_RUNFILES_PATH%": sphinx[DefaultInfo].files_to_run.executable.short_path, 524*60517a1eSAndroid Build Coastguard Worker }, 525*60517a1eSAndroid Build Coastguard Worker is_executable = True, 526*60517a1eSAndroid Build Coastguard Worker ) 527*60517a1eSAndroid Build Coastguard Worker runfiles = ctx.runfiles( 528*60517a1eSAndroid Build Coastguard Worker transitive_files = run_info.source_tree[DefaultInfo].files, 529*60517a1eSAndroid Build Coastguard Worker ).merge(sphinx[DefaultInfo].default_runfiles).merge_all([ 530*60517a1eSAndroid Build Coastguard Worker tool[DefaultInfo].default_runfiles 531*60517a1eSAndroid Build Coastguard Worker for tool in run_info.tools 532*60517a1eSAndroid Build Coastguard Worker ]) 533*60517a1eSAndroid Build Coastguard Worker return [ 534*60517a1eSAndroid Build Coastguard Worker DefaultInfo( 535*60517a1eSAndroid Build Coastguard Worker executable = executable, 536*60517a1eSAndroid Build Coastguard Worker runfiles = runfiles, 537*60517a1eSAndroid Build Coastguard Worker ), 538*60517a1eSAndroid Build Coastguard Worker ] 539*60517a1eSAndroid Build Coastguard Worker 540*60517a1eSAndroid Build Coastguard Workersphinx_run = rule( 541*60517a1eSAndroid Build Coastguard Worker implementation = _sphinx_run_impl, 542*60517a1eSAndroid Build Coastguard Worker doc = """ 543*60517a1eSAndroid Build Coastguard WorkerDirectly run the underlying Sphinx command `sphinx_docs` uses. 544*60517a1eSAndroid Build Coastguard Worker 545*60517a1eSAndroid Build Coastguard WorkerThis is primarily a debugging tool. It's useful for directly running the 546*60517a1eSAndroid Build Coastguard WorkerSphinx command so that debuggers can be attached or output more directly 547*60517a1eSAndroid Build Coastguard Workerinspected without Bazel interference. 548*60517a1eSAndroid Build Coastguard Worker""", 549*60517a1eSAndroid Build Coastguard Worker attrs = { 550*60517a1eSAndroid Build Coastguard Worker "builder": attr.string( 551*60517a1eSAndroid Build Coastguard Worker doc = "The output format to make runnable.", 552*60517a1eSAndroid Build Coastguard Worker default = "html", 553*60517a1eSAndroid Build Coastguard Worker ), 554*60517a1eSAndroid Build Coastguard Worker "docs": attr.label( 555*60517a1eSAndroid Build Coastguard Worker doc = "The {obj}`sphinx_docs` target to make directly runnable.", 556*60517a1eSAndroid Build Coastguard Worker providers = [_SphinxRunInfo], 557*60517a1eSAndroid Build Coastguard Worker ), 558*60517a1eSAndroid Build Coastguard Worker "_template": attr.label( 559*60517a1eSAndroid Build Coastguard Worker allow_single_file = True, 560*60517a1eSAndroid Build Coastguard Worker default = "//sphinxdocs/private:sphinx_run_template.sh", 561*60517a1eSAndroid Build Coastguard Worker ), 562*60517a1eSAndroid Build Coastguard Worker }, 563*60517a1eSAndroid Build Coastguard Worker executable = True, 564*60517a1eSAndroid Build Coastguard Worker) 565