Skip to content

Commit bd09ae2

Browse files
hoe-jocastler
authored andcommitted
[rules score] bugfix rendering of safety analysis
1 parent 8d77504 commit bd09ae2

13 files changed

Lines changed: 180 additions & 22 deletions
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
14+
@startuml safety_analysis_doc_pipeline
15+
16+
' Data flow from safety analysis source files through Bazel rules into the
17+
' Sphinx staging tree.
18+
19+
skinparam linetype ortho
20+
skinparam ArrowFontSize 10
21+
skinparam defaultTextAlignment center
22+
skinparam nodesep 40
23+
skinparam ranksep 50
24+
25+
skinparam rectangle {
26+
BackgroundColor<<src>> #EFF6FB
27+
BorderColor<<src>> #0066B1
28+
BackgroundColor<<gen>> #FFF3E0
29+
BorderColor<<gen>> #EF6C00
30+
BackgroundColor<<rule>> #E8F5E9
31+
BorderColor<<rule>> #2E7D32
32+
BackgroundColor<<stage>> #F3E5F5
33+
BorderColor<<stage>> #7B1FA2
34+
BackgroundColor<<prov>> #FFFDE7
35+
BorderColor<<prov>> #F9A825
36+
}
37+
38+
' ── Inputs ────────────────────────────────────────────────────────────────────
39+
rectangle "failuremodes.trlc\ncontrolmeasures.trlc" <<src>> as trlc
40+
rectangle "fta_*.puml" <<src>> as fta
41+
rectangle "dfa.rst" <<src>> as dfa
42+
43+
' ── fmea rule ─────────────────────────────────────────────────────────────────
44+
rectangle "**fmea**" <<rule>> as fmea
45+
46+
' ── fmea generated files ──────────────────────────────────────────────────────
47+
rectangle "fmea.rst" <<gen>> as fmea_rst
48+
rectangle "failuremodes.inc\ncontrolmeasures.inc" <<gen>> as inc_files
49+
rectangle "fta_*.puml (inlined)\nroot_causes.lobster" <<gen>> as puml_proc
50+
rectangle "detail_*.rst" <<gen>> as detail_rst
51+
52+
' ── fmea SphinxSourcesInfo ────────────────────────────────────────────────────
53+
rectangle "SphinxSourcesInfo\n──────────────────\nsrcs: fmea.rst\ndeps: fmea.rst + *.inc + *.puml\naux_srcs: detail_*.rst" <<prov>> as fmea_ssi
54+
55+
' ── dependability_analysis rule ───────────────────────────────────────────────
56+
rectangle "**dependability_analysis**" <<rule>> as da
57+
58+
' ── dependability_analysis SphinxSourcesInfo ─────────────────────────────────
59+
rectangle "SphinxSourcesInfo\n──────────────────\nsrcs: dfa.rst + fmea.rst\ndeps: + *.inc + *.puml\naux_srcs: detail_*.rst" <<prov>> as da_ssi
60+
61+
' ── dependable_element rule ───────────────────────────────────────────────────
62+
rectangle "**dependable_element**" <<rule>> as de
63+
64+
' ── Sphinx staging tree ───────────────────────────────────────────────────────
65+
rectangle "dependability_analysis/\n dfa.rst ← toctree\n fmea.rst ← toctree\n failuremodes.inc\n controlmeasures.inc\n fta_*.puml\n detail_*.rst ← sub-pages" <<stage>> as stage
66+
67+
' ── Edges ─────────────────────────────────────────────────────────────────────
68+
trlc --> fmea : trlc_rst\nlobster-trlc
69+
fta --> fmea : safety_analysis_tools
70+
71+
fmea --> fmea_rst
72+
fmea --> inc_files
73+
fmea --> puml_proc
74+
fmea --> detail_rst
75+
fmea_rst --> fmea_ssi
76+
inc_files --> fmea_ssi
77+
puml_proc --> fmea_ssi
78+
detail_rst --> fmea_ssi
79+
80+
dfa --> da
81+
fmea_ssi --> da : merge
82+
83+
da --> da_ssi
84+
85+
da_ssi --> de : dependability_analysis attr
86+
de --> stage : symlink srcs+deps\nsymlink aux_srcs\n(no outer toctree entry)
87+
88+
@enduml

bazel/rules/rules_score/docs/tooling_architecture.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,32 @@ self-contained.
261261
├── index.html
262262
├── dep1/ ← merged
263263
└── dep2/ ← merged
264+
265+
.. _safety-analysis-doc-pipeline:
266+
267+
Safety analysis document pipeline
268+
----------------------------------
269+
270+
The diagram below shows how FMEA and FTA source files travel through the three
271+
rules (``fmea`` → ``dependability_analysis`` → ``dependable_element``) and land
272+
in the Sphinx staging tree. Blue boxes are source files authored by the
273+
component team; orange boxes are generated files; yellow boxes are the
274+
``SphinxSourcesInfo`` provider payloads; the purple box is the final staging
275+
directory consumed by Sphinx.
276+
277+
.. uml:: _assets/safety_analysis_doc_pipeline.puml
278+
:align: center
279+
:alt: Safety analysis document pipeline
280+
:width: 100%
281+
282+
``SphinxSourcesInfo`` carries three depsets:
283+
284+
- **srcs** — files that become top-level toctree entries in the enclosing
285+
document section (``fmea.rst``, ``dfa.rst``).
286+
- **deps** — all files that must be present in the staging directory: own
287+
``srcs`` plus ``.inc`` rendered sections and preprocessed ``.puml`` diagrams
288+
that ``fmea.rst`` pulls in via ``.. include::`` / ``.. uml::``.
289+
- **aux_srcs** — files to symlink alongside ``srcs``/``deps`` but **not** added
290+
to the outer index toctree. ``fmea`` uses this for the ``detail_*.rst``
291+
sub-pages, which are referenced from the inner ``.. toctree::`` inside
292+
``fmea.rst`` rather than from the section index.

bazel/rules/rules_score/private/architectural_design.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ def _architectural_design_impl(ctx):
174174
SphinxSourcesInfo(
175175
srcs = sphinx_srcs,
176176
deps = sphinx_srcs,
177+
aux_srcs = depset(),
177178
),
178179
]
179180

bazel/rules/rules_score/private/assumptions_of_use.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def _assumptions_of_use_impl(ctx):
8989
SphinxSourcesInfo(
9090
srcs = all_srcs,
9191
deps = depset(transitive = transitive),
92+
aux_srcs = depset(),
9293
),
9394
]
9495

bazel/rules/rules_score/private/component.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ def _component_impl(ctx):
207207
SphinxSourcesInfo(
208208
srcs = req_sphinx_depset,
209209
deps = sphinx_depset,
210+
aux_srcs = depset(),
210211
),
211212
]
212213

bazel/rules/rules_score/private/dependability_analysis.bzl

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ load("//bazel/rules/rules_score/private:lobster_config.bzl", "format_lobster_sou
3030
# Private Helpers
3131
# ============================================================================
3232

33-
def _collect_analysis_providers(sa, rst_srcs_list, rst_deps_list, lobster_files):
33+
def _collect_analysis_providers(sa, rst_srcs_list, rst_deps_list, rst_aux_list, lobster_files):
3434
"""Collect analysis providers from a single sub-analysis target.
3535
3636
Updates the provided lists/dicts in-place.
@@ -39,12 +39,15 @@ def _collect_analysis_providers(sa, rst_srcs_list, rst_deps_list, lobster_files)
3939
sa: A sub-analysis target (fmea or security).
4040
rst_srcs_list: List of depsets to extend with SphinxSourcesInfo.srcs.
4141
rst_deps_list: List of depsets to extend with SphinxSourcesInfo.deps.
42+
rst_aux_list: List of depsets to extend with SphinxSourcesInfo.aux_srcs.
4243
lobster_files: Dict to update with AnalysisInfo.lobster_files
4344
(canonical name → File).
4445
"""
4546
if SphinxSourcesInfo in sa:
4647
rst_srcs_list.append(sa[SphinxSourcesInfo].srcs)
4748
rst_deps_list.append(sa[SphinxSourcesInfo].deps)
49+
if sa[SphinxSourcesInfo].aux_srcs:
50+
rst_aux_list.append(sa[SphinxSourcesInfo].aux_srcs)
4851
if AnalysisInfo in sa:
4952
lobster_files.update(sa[AnalysisInfo].lobster_files)
5053

@@ -74,6 +77,7 @@ def _dependability_analysis_impl(ctx):
7477

7578
rst_srcs_transitive = [dfa_rst_files]
7679
rst_deps_transitive = [dfa_rst_files]
80+
rst_aux_transitive = []
7781
lobster_files = {} # canonical name → File, merged from all sub-analyses
7882

7983
# -------------------------------------------------------------------------
@@ -82,22 +86,23 @@ def _dependability_analysis_impl(ctx):
8286
fmea_output_files = []
8387
for sa in ctx.attr.fmea:
8488
fmea_output_files.append(sa[DefaultInfo].files)
85-
_collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, lobster_files)
89+
_collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, rst_aux_transitive, lobster_files)
8690

8791
# -------------------------------------------------------------------------
8892
# Collect from security_analysis targets
8993
# -------------------------------------------------------------------------
9094
security_output_files = []
9195
for sa in ctx.attr.security_analysis:
9296
security_output_files.append(sa[DefaultInfo].files)
93-
_collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, lobster_files)
97+
_collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, rst_aux_transitive, lobster_files)
9498

95-
# Architectural design sphinx deps (optional)
96-
if ctx.attr.arch_design and SphinxSourcesInfo in ctx.attr.arch_design:
97-
rst_deps_transitive.append(ctx.attr.arch_design[SphinxSourcesInfo].deps)
99+
# arch_design files are handled separately by dependable_element via its
100+
# architectural_design attribute, so they are not included in this rule's
101+
# sphinx deps to avoid orphan warnings.
98102

99103
all_rst_srcs = depset(transitive = rst_srcs_transitive)
100104
all_rst_deps = depset(transitive = rst_deps_transitive)
105+
all_rst_aux = depset(transitive = rst_aux_transitive) if rst_aux_transitive else depset()
101106

102107
# =========================================================================
103108
# Lobster traceability report (combined FM + CM + FTA)
@@ -182,6 +187,7 @@ def _dependability_analysis_impl(ctx):
182187
SphinxSourcesInfo(
183188
srcs = all_rst_srcs,
184189
deps = all_rst_deps,
190+
aux_srcs = all_rst_aux,
185191
),
186192
]
187193

bazel/rules/rules_score/private/dependable_element.bzl

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,21 @@ def _process_artifact_files(ctx, artifact_name, label):
254254
all_files = _get_sphinx_files(label)
255255
doc_files = _filter_doc_files(all_files)
256256

257-
if not doc_files:
257+
# aux_srcs are files to symlink but NOT add to the outer toctree index.
258+
aux_files = []
259+
if label[SphinxSourcesInfo].aux_srcs:
260+
aux_files = label[SphinxSourcesInfo].aux_srcs.to_list()
261+
262+
if not doc_files and not aux_files:
258263
return (output_files, index_refs)
259264

260265
# Build a lookup of srcs paths so we know which files are toctree entries.
261266
srcs_paths = {f.path: True for f in label[SphinxSourcesInfo].srcs.to_list()}
262267

263-
# Find common directory to preserve hierarchy
264-
common_dir = _find_common_directory(doc_files)
268+
# Find common directory across all files (regular + aux) to preserve hierarchy.
269+
common_dir = _find_common_directory(doc_files + aux_files)
265270

266-
# Process each file
271+
# Process regular deps files
267272
for artifact_file in doc_files:
268273
# Compute paths
269274
relative_path = _compute_relative_path(artifact_file, common_dir)
@@ -289,6 +294,17 @@ def _process_artifact_files(ctx, artifact_name, label):
289294
.replace(".md", "")
290295
index_refs.append(doc_ref)
291296

297+
# Process aux_srcs: symlink without adding to outer toctree index.
298+
for artifact_file in aux_files:
299+
relative_path = _compute_relative_path(artifact_file, common_dir)
300+
output_file = _create_artifact_symlink(
301+
ctx,
302+
artifact_name,
303+
artifact_file,
304+
relative_path,
305+
)
306+
output_files.append(output_file)
307+
292308
return (output_files, index_refs)
293309

294310
def _process_artifact_type(ctx, artifact_name):

bazel/rules/rules_score/private/fmea.bzl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,18 @@ def _fmea_impl(ctx):
282282
lobster_files["root_causes.lobster"] = f
283283

284284
# detail_rsts are NOT top-level toctree entries (they live in sub-toctrees
285-
# within fmea.rst), but they must be present in the Sphinx tree. They go
286-
# into deps so dependable_element symlinks them alongside toctree files.
285+
# within fmea.rst), but they must be symlinked alongside fmea.rst so Sphinx
286+
# can resolve the toctree references. They go into aux_srcs so that
287+
# dependable_element symlinks them without adding them to the outer index.
287288
toctree_files = [f for f in output_files if f not in detail_rsts]
288289
all_sphinx_srcs = depset(toctree_files)
289290

290-
sphinx_deps = [all_sphinx_srcs, depset(detail_rsts)]
291-
if ctx.attr.arch_design and SphinxSourcesInfo in ctx.attr.arch_design:
292-
sphinx_deps.append(ctx.attr.arch_design[SphinxSourcesInfo].deps)
291+
# Only include fmea's own generated files in the sphinx deps. arch_design
292+
# files are handled separately by dependable_element via its
293+
# architectural_design attribute, so omitting them here avoids their RST
294+
# wrappers being symlinked into the dependability_analysis/ section as
295+
# orphaned documents.
296+
sphinx_deps = [all_sphinx_srcs]
293297

294298
return [
295299
DefaultInfo(
@@ -302,6 +306,7 @@ def _fmea_impl(ctx):
302306
SphinxSourcesInfo(
303307
srcs = all_sphinx_srcs,
304308
deps = depset(transitive = sphinx_deps),
309+
aux_srcs = depset(detail_rsts),
305310
),
306311
]
307312

bazel/rules/rules_score/private/requirements.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def _requirements_impl(ctx):
125125
SphinxSourcesInfo(
126126
srcs = sphinx_srcs,
127127
deps = depset(transitive = transitive_sphinx),
128+
aux_srcs = depset(),
128129
),
129130
]
130131

bazel/rules/rules_score/private/sphinx_module.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ def _score_html_impl(ctx):
167167
"json_path": dep[SphinxNeedsInfo].needs_json_file.path, # Use direct file
168168
"id_prefix": "",
169169
"css_class": "",
170+
"version": "1.0",
170171
}
171172
for dep in ctx.attr.deps:
172173
if SphinxModuleInfo in dep:

0 commit comments

Comments
 (0)