|
| 1 | +# ******************************************************************************* |
| 2 | +# Copyright (c) 2026 Contributors to the Eclipse Foundation |
| 3 | +# |
| 4 | +# See the NOTICE file(s) distributed with this work for additional |
| 5 | +# information regarding copyright ownership. |
| 6 | +# |
| 7 | +# This program and the accompanying materials are made available under the |
| 8 | +# terms of the Apache License Version 2.0 which is available at |
| 9 | +# https://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# SPDX-License-Identifier: Apache-2.0 |
| 12 | +# ******************************************************************************* |
| 13 | +"""Rule for filtering execpaths files from a target's output and adapting the matching path. |
| 14 | +Currently using this rule to resolve the input path for breathe's doxygen XML input. |
| 15 | +""" |
| 16 | + |
| 17 | +load("//bazel/rules/rules_score:providers.bzl", "FilteredExecpathInfo") |
| 18 | + |
| 19 | +def _filter_execpath_impl(ctx): |
| 20 | + """Implementation of the filter_execpath rule. |
| 21 | + Iterates over the output files of the target, finds the one matching |
| 22 | + filter_pattern, and computes the resolved path suffix after /bin/. |
| 23 | + """ |
| 24 | + target = ctx.attr.target |
| 25 | + filter_pattern = ctx.attr.filter_pattern |
| 26 | + flag = ctx.attr.flag |
| 27 | + |
| 28 | + # Get all output files from the target |
| 29 | + files = target[DefaultInfo].files.to_list() |
| 30 | + |
| 31 | + # Filter for the path matching the filter pattern |
| 32 | + matched_file = None |
| 33 | + for f in files: |
| 34 | + if filter_pattern in f.path: |
| 35 | + matched_file = f |
| 36 | + break |
| 37 | + if not matched_file: |
| 38 | + all_paths = [f.path for f in files] |
| 39 | + fail("filter_execpath: no path matching '{}' found in outputs of {}. Available paths: {}".format( |
| 40 | + filter_pattern, |
| 41 | + target.label, |
| 42 | + ", ".join(all_paths), |
| 43 | + )) |
| 44 | + |
| 45 | + # Strip the Bazel bin directory prefix from the matched path to get the path |
| 46 | + # relative to the output base. Since _relocate() in sphinx_module symlinks |
| 47 | + # source files under <source_prefix>/<original_path>, the relocated file lives |
| 48 | + # at <source_dir>/<suffix_part> where source_dir is the Sphinx source directory. |
| 49 | + # Breathe resolves breathe_projects paths relative to source_dir (app.srcdir), |
| 50 | + # so we must return just the suffix_part. |
| 51 | + matched_path = matched_file.path |
| 52 | + bin_dir_prefix = ctx.bin_dir.path + "/" |
| 53 | + if matched_path.startswith(bin_dir_prefix): |
| 54 | + suffix_part = matched_path[len(bin_dir_prefix):] |
| 55 | + else: |
| 56 | + suffix_part = matched_path |
| 57 | + resolved_arg = flag + "=" + suffix_part |
| 58 | + return [ |
| 59 | + DefaultInfo(files = depset([matched_file])), |
| 60 | + FilteredExecpathInfo( |
| 61 | + flag = flag, |
| 62 | + resolved_path = suffix_part, |
| 63 | + arg = resolved_arg, |
| 64 | + matched_file = matched_file, |
| 65 | + ), |
| 66 | + ] |
| 67 | + |
| 68 | +_filter_execpath_rule = rule( |
| 69 | + implementation = _filter_execpath_impl, |
| 70 | + attrs = { |
| 71 | + "flag": attr.string( |
| 72 | + mandatory = True, |
| 73 | + doc = "The Sphinx -D flag prefix (e.g. '-Dbreathe_projects.com').", |
| 74 | + ), |
| 75 | + "target": attr.label( |
| 76 | + mandatory = True, |
| 77 | + allow_files = True, |
| 78 | + doc = "The Bazel target whose output files to search.", |
| 79 | + ), |
| 80 | + "filter_pattern": attr.string( |
| 81 | + mandatory = True, |
| 82 | + doc = "Substring to match when filtering the target's output file paths (e.g. 'doxygen_build/xml').", |
| 83 | + ), |
| 84 | + }, |
| 85 | + doc = """Resolve and filter an execpath from a target's outputs at analysis time. |
| 86 | + This rule finds the output file from `target` whose path contains |
| 87 | + `filter_pattern`, strips the Bazel bin directory prefix, and provides |
| 88 | + the result as a FilteredExecpathInfo for consumption by sphinx_module. |
| 89 | + Example usage in BUILD: |
| 90 | + load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", "filter_execpath", "sphinx_module") |
| 91 | + filter_execpath( |
| 92 | + name = "breathe_doxygen_xml", |
| 93 | + flag = "-Dbreathe_projects.doxygen_build", |
| 94 | + target = "//docs/sphinx:doxygen_xml", |
| 95 | + filter_pattern = "doxygen_build/xml", |
| 96 | + ) |
| 97 | + sphinx_module( |
| 98 | + name = "sphinx_doc", |
| 99 | + extra_opts_targets = [":breathe_doxygen_xml"], |
| 100 | + extra_opts = ["-Dbreathe_default_project=doxygen_build"], |
| 101 | + ... |
| 102 | + ) |
| 103 | + """, |
| 104 | +) |
| 105 | + |
| 106 | +def filter_execpath(name, flag, target, filter_pattern, **kwargs): |
| 107 | + """Resolve and filter an execpath from a target's outputs at analysis time. |
| 108 | + Args: |
| 109 | + name: Name for this target. |
| 110 | + flag: The Sphinx -D flag prefix (e.g. "-Dbreathe_projects.doxygen_build"). |
| 111 | + target: The Bazel label whose output files to search. |
| 112 | + filter_pattern: Substring to match when filtering the target's output file paths. |
| 113 | + **kwargs: Additional keyword arguments passed to the underlying rule (e.g. visibility). |
| 114 | + """ |
| 115 | + _filter_execpath_rule( |
| 116 | + name = name, |
| 117 | + flag = flag, |
| 118 | + target = target, |
| 119 | + filter_pattern = filter_pattern, |
| 120 | + **kwargs |
| 121 | + ) |
0 commit comments