@@ -103,6 +103,7 @@ def sphinx_docs(
103103 strip_prefix = "" ,
104104 extra_opts = [],
105105 tools = [],
106+ allow_persistent_workers = True ,
106107 ** kwargs ):
107108 """Generate docs using Sphinx.
108109
@@ -142,6 +143,9 @@ def sphinx_docs(
142143 tools: {type}`list[label]` Additional tools that are used by Sphinx and its plugins.
143144 This just makes the tools available during Sphinx execution. To locate
144145 them, use {obj}`extra_opts` and `$(location)`.
146+ allow_persistent_workers: {type}`bool` (experimental) If true, allow
147+ using persistent workers for running Sphinx, if Bazel decides to do so.
148+ This can improve incremental building of docs.
145149 **kwargs: {type}`dict` Common attributes to pass onto rules.
146150 """
147151 add_tag (kwargs , "@rules_python//sphinxdocs:sphinx_docs" )
@@ -165,6 +169,7 @@ def sphinx_docs(
165169 source_tree = internal_name + "/_sources" ,
166170 extra_opts = extra_opts ,
167171 tools = tools ,
172+ allow_persistent_workers = allow_persistent_workers ,
168173 ** kwargs
169174 )
170175
@@ -209,6 +214,7 @@ def _sphinx_docs_impl(ctx):
209214 source_path = source_dir_path ,
210215 output_prefix = paths .join (ctx .label .name , "_build" ),
211216 inputs = inputs ,
217+ allow_persistent_workers = ctx .attr .allow_persistent_workers ,
212218 )
213219 outputs [format ] = output_dir
214220 per_format_args [format ] = args_env
@@ -229,6 +235,10 @@ def _sphinx_docs_impl(ctx):
229235_sphinx_docs = rule (
230236 implementation = _sphinx_docs_impl ,
231237 attrs = {
238+ "allow_persistent_workers" : attr .bool (
239+ doc = "(experimental) Whether to invoke Sphinx as a persistent worker." ,
240+ default = False ,
241+ ),
232242 "extra_opts" : attr .string_list (
233243 doc = "Additional options to pass onto Sphinx. These are added after " +
234244 "other options, but before the source/output args." ,
@@ -254,28 +264,45 @@ _sphinx_docs = rule(
254264 },
255265)
256266
257- def _run_sphinx (ctx , format , source_path , inputs , output_prefix ):
267+ def _run_sphinx (ctx , format , source_path , inputs , output_prefix , allow_persistent_workers ):
258268 output_dir = ctx .actions .declare_directory (paths .join (output_prefix , format ))
259269
260270 run_args = [] # Copy of the args to forward along to debug runner
261271 args = ctx .actions .args () # Args passed to the action
262272
273+ # An args file is required for persistent workers, but we don't know if
274+ # the action will use worker mode or not (settings we can't see may
275+ # force non-worker mode). For consistency, always use a params file.
276+ args .use_param_file ("@%s" , use_always = True )
277+ args .set_param_file_format ("multiline" )
278+
279+ # NOTE: sphinx_build.py relies on the first two args being the srcdir and
280+ # outputdir, in that order.
281+ args .add (source_path )
282+ args .add (output_dir .path )
283+
263284 args .add ("--show-traceback" ) # Full tracebacks on error
264285 run_args .append ("--show-traceback" )
265- args .add ("--builder" , format )
266- run_args .extend (( "--builder" , format ))
286+ args .add (format , format = "--builder=%s" )
287+ run_args .append ( "--builder={}" . format ( format ))
267288
268289 if ctx .attr ._quiet_flag [BuildSettingInfo ].value :
269290 # Not added to run_args because run_args is for debugging
270291 args .add ("--quiet" ) # Suppress stdout informational text
271292
272293 # Build in parallel, if possible
273294 # Don't add to run_args: parallel building breaks interactive debugging
274- args .add ("--jobs" , "auto" )
275- args .add ("--fresh-env" ) # Don't try to use cache files. Bazel can't make use of them.
276- run_args .append ("--fresh-env" )
277- args .add ("--write-all" ) # Write all files; don't try to detect "changed" files
278- run_args .append ("--write-all" )
295+ args .add ("--jobs=auto" )
296+
297+ # Put the doctree dir outside of the output directory.
298+ # This allows it to be reused between invocations when possible; Bazel
299+ # clears the output directory every action invocation.
300+ # * For workers, they can fully re-use it.
301+ # * For non-workers, it can be reused when sandboxing is disabled via
302+ # the `no-sandbox` tag or execution requirement.
303+ #
304+ # We also use a non-dot prefixed name so it shows up more visibly.
305+ args .add (paths .join (output_dir .path + "_doctrees" ), format = "--doctree-dir=%s" )
279306
280307 for opt in ctx .attr .extra_opts :
281308 expanded = ctx .expand_location (opt )
@@ -287,9 +314,6 @@ def _run_sphinx(ctx, format, source_path, inputs, output_prefix):
287314 for define in extra_defines :
288315 run_args .extend (("--define" , define ))
289316
290- args .add (source_path )
291- args .add (output_dir .path )
292-
293317 env = dict ([
294318 v .split ("=" , 1 )
295319 for v in ctx .attr ._extra_env_flag [_FlagInfo ].value
@@ -299,6 +323,14 @@ def _run_sphinx(ctx, format, source_path, inputs, output_prefix):
299323 for tool in ctx .attr .tools :
300324 tools .append (tool [DefaultInfo ].files_to_run )
301325
326+ # NOTE: Command line flags or RBE capabilities may override the execution
327+ # requirements and disable workers. Thus, we can't assume that these
328+ # exec requirements will actually be respected.
329+ execution_requirements = {}
330+ if allow_persistent_workers :
331+ execution_requirements ["supports-workers" ] = "1"
332+ execution_requirements ["requires-worker-protocol" ] = "json"
333+
302334 ctx .actions .run (
303335 executable = ctx .executable .sphinx ,
304336 arguments = [args ],
@@ -308,6 +340,7 @@ def _run_sphinx(ctx, format, source_path, inputs, output_prefix):
308340 mnemonic = "SphinxBuildDocs" ,
309341 progress_message = "Sphinx building {} for %{{label}}" .format (format ),
310342 env = env ,
343+ execution_requirements = execution_requirements ,
311344 )
312345 return output_dir , struct (args = run_args , env = env )
313346
0 commit comments