Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -291,14 +291,14 @@ use_repo(pip, "manual_analysis_deps")
bazel_dep(name = "trlc", version = "0.0.0")
git_override(
module_name = "trlc",
commit = "7f06d0396fd0e5f02b657d25700775af5113b7e0",
commit = "c4c531b9d667085daa09dfc1590edacc314bfda4",
remote = "https://github.com/bmw-software-engineering/trlc.git",
)

bazel_dep(name = "lobster", version = "0.0.0")
git_override(
module_name = "lobster",
commit = "9da4a491ef65c33f50e82d1e29346180dd5acc60",
commit = "2792e2daee2cf524fdc7b1545fd3537791ebc36c",
remote = "https://github.com/bmw-software-engineering/lobster.git",
)

Expand Down
1 change: 1 addition & 0 deletions bazel/rules/rules_score/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ sphinx_module(
"//plantuml/sphinx/clickable_plantuml:readme_md": "bazel/rules/rules_score/docs/tool_reference/clickable_plantuml.md",
"//lobster_bazel:readme_md": "bazel/rules/rules_score/docs/tool_reference/lobster_bazel.md",
"//manual_analysis:readme_md": "bazel/rules/rules_score/docs/tool_reference/manual_analysis.md",
"//validation/core/docs/requirements:requirements_rst": "bazel/rules/rules_score/docs/requirements/validation_core_requirements_rst.rst",
},
visibility = ["//visibility:public"],
)
Expand Down
5 changes: 4 additions & 1 deletion bazel/rules/rules_score/docs/rule_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ cross-module dependencies and automatic HTML merging.
- no
- Bazel visibility

**Generated targets:** ``<name>`` — Sphinx build; output under ``bazel-bin/<name>/html/``
**Generated targets:**

- ``<name>`` — HTML output under ``bazel-bin/<name>/html/``; use in ``deps`` of other ``sphinx_module`` targets.
- ``<name>_needs`` — ``needs.json`` produced by the needs builder; consumed transitively by downstream modules for cross-module ``{requirement:downstream-ref}`` resolution. See :ref:`two-phase-sphinx-build` for the full data-flow description.


Artifact Rules
Expand Down
1 change: 1 addition & 0 deletions bazel/rules/rules_score/docs/tool_qualification.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ Requirements
Use Cases <requirements/use_cases_rst>
Potential Errors <requirements/potential_errors_rst>
Tool Requirements <requirements/tool_requirements_rst>
Validation Core Requirements <requirements/validation_core_requirements_rst>
100 changes: 96 additions & 4 deletions bazel/rules/rules_score/docs/tooling_architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,20 @@ are rendered under :doc:`tool_reference/index`.
* - **Sphinx (Docs)**
- ``score_build`` (``src/sphinx_wrapper.py``),
``html_merge_tool`` (``src/sphinx_html_merge.py``),
``sphinx_module_ext`` / ``bazel_sphinx_needs``
``sphinx_module_ext``,
``trlc`` Sphinx extension (``@trlc``)
- ``sphinx_module``, ``dependable_element``
- Two-phase documentation build: phase 1 emits ``needs.json`` for
sphinx-needs cross-referencing, phase 2 builds HTML and merges
dependency modules into one site.
- Two-phase documentation build: **phase 1** (``<name>_needs`` target)
runs Sphinx with ``--builder needs`` to emit ``needs.json`` containing
any native ``sphinx-needs`` (``.. need::``) directives found in the
sources. **Phase 2** (``<name>`` target) runs Sphinx with
``--builder html``, resolving ``trlc`` ``.. requirement:definition::``
cross-references within the relocated source tree and consuming
``needs.json`` files of all ``deps`` via ``needs_external_needs_json``
for cross-module ``sphinx-needs`` links.
``src/sphinx_html_merge.py`` then merges dependency HTML directories
into the final output site.
See :ref:`two-phase-sphinx-build` for details.
* - **Lobster Bazel**
- ``//lobster_bazel:lobster_linker`` (``parse_source_files.py``)
- ``rules_score_impl`` (tool-qualification chain)
Expand Down Expand Up @@ -169,3 +178,86 @@ feed that pipeline:
* **FMEA** (``failuremodes.trlc`` / ``controlmeasures.trlc``) → ``lobster-trlc``;
**FTA** (``fta.puml``) → ``safety_analysis_tools`` → ``root_causes.lobster``.
* **Unit tests** (gtest) → ``gtest_report`` → ``<unit>.lobster``.

.. _two-phase-sphinx-build:

Two-phase Sphinx build
----------------------

Every ``sphinx_module`` call expands into **two** Bazel targets that run
sequentially:

.. code-block:: text

<name>_needs (phase 1 — needs builder)
<name> (phase 2 — HTML builder)

Phase 1 — ``<name>_needs``
~~~~~~~~~~~~~~~~~~~~~~~~~~

Sphinx is invoked with ``--builder needs`` against the **static docs/ source
tree** (only ``srcs`` files — the checked-in ``.rst``/``.md`` files plus the
generated ``trlc_rst`` outputs that are listed as label targets in ``srcs``).
Generated/external files from ``renamed_srcs`` and ``docs_library_deps`` are
**not** included; their toctree entries produce ``toc.not_readable`` warnings
that are suppressed in ``conf.template.py`` (see below for why this is safe).

The needs builder scans every document for ``.. need::`` directives
(``sphinx-needs`` native format) and writes them to a ``needs.json`` file.

The ``toc.not_readable`` suppression in ``conf.template.py`` is safe for the
HTML phase because that phase relocates every file into a staging directory, so
it never encounters an unresolvable toctree entry.

The resulting ``needs.json`` (empty for modules whose requirements are authored
in TRLC rather than native ``sphinx-needs`` format) is wrapped in a
``SphinxNeedsInfo`` provider and propagated transitively so that every
downstream module can consume it.

Phase 2 — ``<name>``
~~~~~~~~~~~~~~~~~~~~

Sphinx is invoked with ``--builder html`` against a **relocated copy** of all
source files (``srcs``, ``renamed_srcs``, ``docs_library_deps``) symlinked into
a unified staging directory under ``bazel-bin/``.

This is also where ``.. requirement:definition::`` directives (from the ``trlc``
Sphinx extension) are processed and cross-references resolved. The raw
requirement records come from ``.trlc`` source files compiled by the
``trlc_rst`` Bazel rule into ``.rst`` files that contain the directives. The
chain is:

.. code-block:: text

*.trlc
└─ trlc_rst (Bazel rule, @trlc)
└─ requirements_rst.rst (.. requirement:definition:: <ID> ...)
└─ Sphinx HTML builder (resolves {requirement:downstream-ref})

Before the HTML build starts, ``sphinx_module_ext.py`` reads the aggregated
``needs_external_needs.json``
(written by the Bazel rule from all incoming ``SphinxNeedsInfo`` providers) and
populates the ``needs_external_needs`` Sphinx configuration key. This tells
``sphinx-needs`` where to find the ``needs.json`` of each dependency and what
base URL to use for generated hyperlinks, so a ``{requirement:downstream-ref}``
role in a spec file can link directly to the requirement definition page in the
dependency's HTML.

After the HTML build, ``src/sphinx_html_merge.py`` copies each dependency's
output directory into ``<name>/html/<dep-name>/`` so the final site is
self-contained.

.. code-block:: text

deps[*].needs.json ──► needs_external_needs.json
sources (all relocated) ───────┤
Sphinx HTML builder
<name>/_html/ ──► sphinx_html_merge
<name>/html/
├── index.html
├── dep1/ ← merged
└── dep2/ ← merged
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ to-string-rules:
to-string:
- $(item)@$(version)
- $(item)
- package: ScoreReq
tuple-type: CompReqSourceId
to-string:
- $(item)@$(version)
- $(item)
conversion-rules:
- package: ScoreReq
record-type: CompReq
Expand Down
10 changes: 9 additions & 1 deletion bazel/rules/rules_score/private/sphinx_module.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@ def _score_needs_impl(ctx):
# Get config file (generate or use provided)
config_file = _create_config_py(ctx)

# Phase 1: Build needs.json (without external needs)
# Phase 1: Build needs.json (without external needs).
# The needs builder (sphinx-needs NeedsBuilder) only collects `.. need::`
# directives — it is blind to the custom trlc `RequirementsDomain` and its
# `.. requirement:definition::` directives. Generated/external files
# (renamed_srcs, docs_library_deps) are therefore not needed here. Their
# toctree entries would produce toc.not_readable warnings because Sphinx's
# source root is the original docs/ checkout; those are suppressed in
# conf.template.py (safe: the HTML phase relocates everything so it never
# emits toc.not_readable).
needs_inputs = ctx.files.srcs + [config_file]
needs_args = [
"--index_file",
Expand Down
11 changes: 11 additions & 0 deletions bazel/rules/rules_score/templates/conf.template.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@
"**/*_design",
]

# Suppress toctree warnings for documents absent from the needs builder's source
# tree. The needs builder runs against only the static docs/ checkout; generated
# files (trlc_rst outputs, renamed_srcs, docs_library_deps) live in bazel-out/
# and are invisible to it. Their toctree references produce toc.not_readable
# warnings that are cosmetic: the needs builder (sphinx-needs NeedsBuilder)
# captures only `.. need::` directives, not trlc `.. requirement:definition::`
# directives, so needs.json content is unaffected by missing files.
# This suppression is safe for the HTML phase because that phase relocates every
# file into a unified staging directory, so it never encounters toc.not_readable.
suppress_warnings = ["toc.not_readable"]

# Enable markdown rendering
source_suffix = {
".rst": "restructuredtext",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,15 @@ tuple FeatReqId {
version Integer
}

tuple CompReqSourceId {
item [FeatReq, AssumedSystemReq]
separator @
version Integer
}

type CompReq "Component-level requirement allocated to a specific software component." extends RequirementSafety {
derived_from "Versioned references to the FeatReq items this component requirement is derived from. Omit only for component-internal requirements with no feature-level parent."
optional FeatReqId[1 .. *]
derived_from "Versioned references to the FeatReq or AssumedSystemReq items this component requirement is derived from. Omit only for component-internal requirements with no feature-level parent."
optional CompReqSourceId[1 .. *]
mitigates "Reference to the FailureMode or safety concern that this requirement mitigates."
optional String
}
Expand Down
4 changes: 2 additions & 2 deletions lobster_bazel/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,8 @@ smmap==5.0.3 \
--hash=sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c \
--hash=sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f
# via gitdb
trlc==2.0.3 \
--hash=sha256:86b4c8690239f7b993259b316b8d6079633e32dff8f8ddbae5ad61503ac9362d
trlc==2.0.5 \
--hash=sha256:fc4e4e3a21980711e85426d840f4f7f2e6ebb29ce94d55004f7d8e5e4532426c
# via bmw-lobster-tool-trlc
urllib3==2.7.0 \
--hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \
Expand Down
Loading