@@ -19,6 +19,39 @@ load("@rules_python//sphinxdocs:sphinx_docs_library.bzl", "sphinx_docs_library")
1919load ("@rules_python//sphinxdocs/private:sphinx_docs_library_info.bzl" , "SphinxDocsLibraryInfo" )
2020load ("//bazel/rules/rules_score:providers.bzl" , "SphinxModuleInfo" , "SphinxNeedsInfo" )
2121
22+ # Delimiter used to encode filter_execpath parameters into a single extra_opts string.
23+ _FILTER_EXECPATH_DELIM = "@@FILTER_EXECPATH@@"
24+
25+ def filter_execpath (flag , label , filter_pattern ):
26+ """Construct an extra_opts entry that filters execpaths and rewrites them with the relocated target prefix.
27+
28+ When used in extra_opts of sphinx_module, the rule implementation will:
29+ 1. Expand $(execpaths <label>) to get all output paths
30+ 2. Filter for the path containing <filter_pattern>
31+ 3. Rewrite the path to include the target's relocated prefix
32+ (e.g. bazel-out/.../bin/<package>/<name>/<original_path>)
33+
34+ Example usage in BUILD:
35+ load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", "filter_execpath", "sphinx_module")
36+
37+ sphinx_module(
38+ name = "sphinx_doc",
39+ extra_opts = [
40+ filter_execpath("-Dbreathe_projects.com", "//docs/sphinx:doxygen_xml", "doxygen_build/xml"),
41+ ],
42+ ...
43+ )
44+
45+ Args:
46+ flag: The Sphinx -D flag prefix (e.g. "-Dbreathe_projects.com")
47+ label: The Bazel label whose execpaths to expand (e.g. "//docs/sphinx:doxygen_xml")
48+ filter_pattern: Substring to match when filtering the execpaths (e.g. "doxygen_build/xml")
49+
50+ Returns:
51+ A specially formatted string that the sphinx_module rule will process.
52+ """
53+ return _FILTER_EXECPATH_DELIM .join ([flag , str (label ), filter_pattern ])
54+
2255def _create_config_py (ctx ):
2356 """Get or generate the conf.py configuration file.
2457
@@ -119,11 +152,55 @@ def _score_html_impl(ctx):
119152 # Expand location references in extra_opts and collect as sphinx arguments.
120153 # targets must include all labels referenced via $(location ...) / $(execpaths ...).
121154 extra_opts_targets = ctx .attr .srcs + ctx .attr .docs_library_deps
155+ source_prefix = ctx .label .name
156+
122157 for opt in ctx .attr .extra_opts :
123- expanded = ctx .expand_location (opt , targets = extra_opts_targets )
124- print ("???????? expanded opts location: " , expanded )
125- args .add (expanded )
126- run_args .append (expanded )
158+ if _FILTER_EXECPATH_DELIM in opt :
159+ # Process filter_execpath() encoded strings:
160+ # Format: flag@@FILTER_EXECPATH@@label@@FILTER_EXECPATH@@filter_pattern
161+ parts = opt .split (_FILTER_EXECPATH_DELIM )
162+ flag = parts [0 ]
163+ label_str = parts [1 ]
164+ filter_pattern = parts [2 ]
165+
166+ # Expand execpaths for the referenced label
167+ expanded = ctx .expand_location ("$(execpaths " + label_str + ")" , targets = extra_opts_targets )
168+ expanded_paths = expanded .split (" " )
169+
170+ # Filter for the path matching the filter pattern
171+ matched_path = None
172+ for p in expanded_paths :
173+ if filter_pattern in p :
174+ matched_path = p
175+ break
176+
177+ if not matched_path :
178+ fail ("filter_execpath: no path matching '{}' found in execpaths of {}. Paths: {}" .format (
179+ filter_pattern ,
180+ label_str ,
181+ expanded ,
182+ ))
183+
184+ # Extract the path suffix after "/bin/" — this is the original path relative
185+ # to the output base. Since _relocate() symlinks source files under
186+ # <source_prefix>/<original_path>, the relocated file lives at
187+ # <source_dir>/<suffix_part> where source_dir is the Sphinx source directory.
188+ # Breathe resolves breathe_projects paths relative to source_dir (app.srcdir),
189+ # so we must return just the suffix_part.
190+ bin_marker = "/bin/"
191+ bin_idx = matched_path .find (bin_marker )
192+ if bin_idx >= 0 :
193+ suffix_part = matched_path [bin_idx + len (bin_marker ):]
194+ else :
195+ suffix_part = matched_path
196+
197+ expanded_opt = flag + "=" + suffix_part
198+ else :
199+ # Standard extra_opts: expand locations and pass through
200+ expanded_opt = ctx .expand_location (opt , targets = extra_opts_targets )
201+
202+ args .add (expanded_opt )
203+ run_args .append (expanded_opt )
127204
128205 # Collect all transitive dependencies with deduplication
129206 modules = []
@@ -150,7 +227,6 @@ def _score_html_impl(ctx):
150227 content = json .encode_indent (needs_external_needs , indent = " " ),
151228 )
152229
153- source_prefix = ctx .label .name
154230 sphinx_source_files = []
155231
156232 # Materialize a file under the `_sources` dir
@@ -281,8 +357,8 @@ _score_html = rule(
281357 doc = "Submodule symbols.needs targets for this module." ,
282358 ),
283359 extra_opts = attr .string_list (
284- doc = "Additional options to pass onto Sphinx. These are added after " +
285- "other options, but before the source/output args." ,
360+ doc = "Additional options to pass onto Sphinx. These are added after " +
361+ "other options, but before the source/output args." ,
286362 ),
287363 ),
288364 toolchains = ["//bazel/rules/rules_score:toolchain_type" ],
0 commit comments