Skip to content
Open
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
3 changes: 2 additions & 1 deletion bazel/rules/rules_score/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ architectural_design(
**`bazel build`** — runs `puml_parser` on every `.puml` file, producing:
- a `.fbs.bin` FlatBuffers binary (diagram AST) — consumed by validation/core checks
- a `.lobster` traceability file (Interface elements only) — consumed by LOBSTER
- a `plantuml_links.json` — consumed by the `clickable_plantuml` Sphinx extension
- a `validation.log` from the `architectural-design` validation profile
- a `.idmap.json` sidecar — consumed by the `clickable_plantuml` Sphinx extension
to resolve cross-diagram links based on element *defines/references* roles

Diagrams in `public_api` are classified separately so their lobster items flow
through `public_api_lobster_files` for failure-mode traceability.
Expand Down
2 changes: 1 addition & 1 deletion bazel/rules/rules_score/docs/_assets/tooling_chain.puml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ rectangle "sphinx_module" <<rule>> as sphinx
' ── Tools ─────────────────────────────────────────────────────────────────────
rectangle "**TRLC**\ntrlc parser + trlc_rst\n(.trlc/.rsl -> .rst; TRLCRST lib)" <<tool>> as trlc
rectangle "**rst_to_trlc**\n(.rst -> .trlc)" <<own>> as r2t
rectangle "**PlantUML Parser**\nparser + linker (Rust)\n(.puml -> .fbs.bin + .lobster)" <<tool>> as puml
rectangle "**PlantUML Parser**\npuml_cli (Rust)\n(.puml -> .fbs.bin + .lobster + .idmap.json)" <<tool>> as puml
rectangle "**puml_cli** (FTA mode)\ninline metamodel + extract\n(.puml -> inlined .puml +\nfta_chains.json + root_causes.lobster)" <<own>> as fta
rectangle "**fmea_assembler**\nTRLCRST page build\n(.trlc + chains -> fmea.rst)" <<own>> as asm
rectangle "**Lobster**\nlobster-trlc / -report /\n-ci-report / gtest_report" <<tool>> as lob
Expand Down
20 changes: 15 additions & 5 deletions bazel/rules/rules_score/docs/tooling_architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,14 @@ are rendered under :doc:`tool_reference/index`.
- Converts RST requirement directives (``feat_req``, ``comp_req``, …) into
``.trlc`` records so requirements can be authored in either RST or TRLC.
* - **PlantUML Parser**
- ``@score_tooling//plantuml/parser:parser`` (Rust) + ``:linker``
- ``@score_tooling//plantuml/parser:parser`` (Rust)
- ``architectural_design``, ``unit_design``
- Parses ``.puml`` diagrams into a FlatBuffers AST (``.fbs.bin``, one
``root_type`` per diagram kind) and extracts interface ``.lobster``
items. The **linker** merges the FlatBuffers into ``plantuml_links.json``
for the ``clickable_plantuml`` Sphinx extension. Rejects syntactically
invalid diagrams with a non-zero exit code.
``root_type`` per diagram kind), extracts interface ``.lobster`` items,
and emits ``.idmap.json`` sidecars recording the *defines/references*
roles of each element. The ``clickable_plantuml`` Sphinx extension reads
these sidecars to resolve cross-diagram links without a separate linker
step. Rejects syntactically invalid diagrams with a non-zero exit code.
* - **puml_cli (FTA mode)**
- ``//plantuml/parser/puml_cli`` ``--fta-output-dir`` (Rust; FTA model in
the ``puml_fta`` crate)
Expand All @@ -121,6 +122,15 @@ are rendered under :doc:`tool_reference/index`.
parse: an overview table, one section per failure mode (detail + inline
fault tree + that chain's control measures), and trailing "Unlinked"
sections so nothing is dropped.
* - **safety_analysis_tools**
- ``//bazel/rules/rules_score:safety_analysis_tools``
(``src/safety_analysis_tools.py``, local)
- ``fmea``
- Assembles the failure-mode-centric ``fmea.rst`` from ``fta_chains.json``
plus the FailureMode / ControlMeasure records in one in-process TRLC
parse: an overview table, one section per failure mode (detail + inline
fault tree + that chain's control measures), and trailing "Unlinked"
sections so nothing is dropped.
* - **Lobster**
- ``@lobster//`` : ``lobster-trlc``, ``lobster-report``,
``lobster-ci-report``, ``lobster-html-report``, ``gtest_report``,
Expand Down
177 changes: 82 additions & 95 deletions bazel/rules/rules_score/private/architectural_design.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,23 @@ load("//bazel/rules/rules_score/private:verbosity.bzl", "VERBOSITY_ATTR", "get_l
# ============================================================================

def _run_puml_parser(ctx, puml_file):
"""Run the PlantUML parser on a single .puml file to produce a FlatBuffers binary
and a lobster traceability file.
"""Run the PlantUML parser on a single .puml file.

The diagram type is auto-detected by the parser and encoded in the
FlatBuffers schema (each diagram type uses its own root_type).
Lobster output is produced in-process for component diagrams.
Produces three output files:
- a FlatBuffers binary (``.fbs.bin``),
- a LOBSTER traceability file (``.lobster``), and
- an idmap sidecar (``.idmap.json``) used by the
``clickable_plantuml`` Sphinx extension to resolve cross-diagram links.

``puml_file.short_path`` (workspace-relative) is passed as ``--source-name``
so the idmap ``source`` field is a stable, path-unique identifier.

Args:
ctx: Rule context
puml_file: The .puml File object to parse

Returns:
Tuple of (fbs_output, lobster_output) declared output Files.
Tuple of (fbs_output, lobster_output, idmap_output) declared output Files.
"""
file_stem = puml_file.basename.rsplit(".", 1)[0]
fbs_output = ctx.actions.declare_file(
Expand All @@ -53,25 +57,32 @@ def _run_puml_parser(ctx, puml_file):
lobster_output = ctx.actions.declare_file(
"{}/{}.lobster".format(ctx.label.name, file_stem),
)
idmap_output = ctx.actions.declare_file(
"{}/{}.idmap.json".format(ctx.label.name, file_stem),
)

ctx.actions.run(
inputs = [puml_file],
outputs = [fbs_output, lobster_output],
outputs = [fbs_output, lobster_output, idmap_output],
executable = ctx.executable._puml_parser,
arguments = [
"--file",
puml_file.path,
"--source-name",
puml_file.short_path,
"--fbs-output-dir",
fbs_output.dirname,
"--lobster-output-dir",
lobster_output.dirname,
"--idmap-output-dir",
idmap_output.dirname,
"--log-level",
get_log_level(ctx),
],
progress_message = "Parsing PlantUML diagram: %s" % puml_file.short_path,
)

return fbs_output, lobster_output
return fbs_output, lobster_output, idmap_output

def _parse_puml_diagrams(ctx, files):
"""Run the PlantUML parser on all .puml/.plantuml files in a list.
Expand All @@ -81,16 +92,18 @@ def _parse_puml_diagrams(ctx, files):
files: List of File objects

Returns:
Tuple of (fbs_outputs, lobster_outputs) lists of generated Files.
Tuple of (fbs_outputs, lobster_outputs, idmap_outputs) lists of generated Files.
"""
fbs_outputs = []
lobster_outputs = []
idmap_outputs = []
for f in files:
if f.extension in ("puml", "plantuml"):
fbs, lobster = _run_puml_parser(ctx, f)
fbs, lobster, idmap = _run_puml_parser(ctx, f)
fbs_outputs.append(fbs)
lobster_outputs.append(lobster)
return fbs_outputs, lobster_outputs
idmap_outputs.append(idmap)
return fbs_outputs, lobster_outputs, idmap_outputs

def _run_validation(ctx, component_fbs_files, sequence_fbs_files, internal_api_fbs_files):
"""Run the architectural-design validation profile.
Expand Down Expand Up @@ -138,45 +151,25 @@ def _architectural_design_impl(ctx):
"""

# Parse each architectural view separately so each provider field carries
# the flatbuffers for its own category.
static_fbs_list, static_lobster_list = _parse_puml_diagrams(ctx, ctx.files.static)
dynamic_fbs_list, dynamic_lobster_list = _parse_puml_diagrams(ctx, ctx.files.dynamic)
public_api_fbs_list, public_api_lobster_list = _parse_puml_diagrams(ctx, ctx.files.public_api)
internal_api_fbs_list, _internal_api_lobster_list = _parse_puml_diagrams(ctx, ctx.files.internal_api)
# the flatbuffers (and idmap sidecars) for its own category.
static_fbs_list, static_lobster_list, static_idmap_list = _parse_puml_diagrams(ctx, ctx.files.static)
dynamic_fbs_list, dynamic_lobster_list, dynamic_idmap_list = _parse_puml_diagrams(ctx, ctx.files.dynamic)
public_api_fbs_list, public_api_lobster_list, public_api_idmap_list = _parse_puml_diagrams(ctx, ctx.files.public_api)
internal_api_fbs_list, _internal_api_lobster_list, internal_api_idmap_list = _parse_puml_diagrams(ctx, ctx.files.internal_api)

static_fbs = depset(static_fbs_list)
dynamic_fbs = depset(dynamic_fbs_list)
public_api_fbs = depset(public_api_fbs_list)
internal_api_fbs = depset(internal_api_fbs_list)
public_api_lobster = depset(public_api_lobster_list)
all_idmaps = depset(static_idmap_list + dynamic_idmap_list + public_api_idmap_list + internal_api_idmap_list)

# Source files for SphinxSourcesInfo (sphinx documentation pipeline)
all_source_files = depset(
transitive = [depset(ctx.files.static), depset(ctx.files.dynamic), depset(ctx.files.public_api), depset(ctx.files.internal_api)],
)

# Run the linker on all generated .fbs.bin files to produce a
# plantuml_links.json for the clickable_plantuml Sphinx extension.
all_fbs_files = static_fbs.to_list() + dynamic_fbs.to_list() + public_api_fbs.to_list() + internal_api_fbs.to_list()
plantuml_links_json = ctx.actions.declare_file(
"{}/plantuml_links.json".format(ctx.label.name),
)
if all_fbs_files:
ctx.actions.run(
inputs = all_fbs_files,
outputs = [plantuml_links_json],
executable = ctx.executable._linker,
arguments = ["--fbs-files"] + [f.path for f in all_fbs_files] + ["--output", plantuml_links_json.path, "--log-level", get_log_level(ctx)],
progress_message = "Generating PlantUML links JSON for %s" % ctx.label.name,
)
else:
ctx.actions.write(
output = plantuml_links_json,
content = '{"links":[]}',
)

sphinx_files = depset(
[plantuml_links_json],
transitive = [all_source_files],
)

Expand All @@ -199,7 +192,9 @@ def _architectural_design_impl(ctx):
sphinx_srcs = depset(rst_wrappers, transitive = [sphinx_files])

return [
DefaultInfo(files = depset([validation_log.file], transitive = [all_source_files])),
# DefaultInfo intentionally carries only authored source artifacts.
# idmap sidecars are Sphinx-only auxiliaries exposed via SphinxSourcesInfo.
DefaultInfo(files = all_source_files),
ArchitecturalDesignInfo(
static = static_fbs,
dynamic = dynamic_fbs,
Expand All @@ -208,77 +203,69 @@ def _architectural_design_impl(ctx):
public_api_lobster_files = public_api_lobster,
validation_logs = [validation_log],
),
# Source diagram files + plantuml_links.json for the sphinx documentation build
# Source diagrams are regular srcs/deps; .idmap.json sidecars are aux files
# needed by clickable_plantuml and must not become top-level toctree entries.
SphinxSourcesInfo(
srcs = sphinx_srcs,
deps = sphinx_srcs,
aux_srcs = depset(),
aux_srcs = all_idmaps,
),
]

# ============================================================================
# Rule Definition
# ============================================================================

def _architectural_design_attrs():
attrs = {
"static": attr.label_list(
allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"],
mandatory = False,
doc = "Static architecture diagrams (class diagrams, component diagrams, etc.)",
),
"dynamic": attr.label_list(
allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"],
mandatory = False,
doc = "Dynamic architecture diagrams (sequence diagrams, activity diagrams, etc.)",
),
"public_api": attr.label_list(
allow_files = [".puml", ".plantuml"],
mandatory = False,
doc = "Public API diagrams (parsed identically to static/dynamic). " +
"Classified separately so their lobster items are exposed via " +
"public_api_lobster_files, enabling failure-mode-to-interface " +
"traceability at the dependable element level.",
),
"internal_api": attr.label_list(
allow_files = [".puml", ".plantuml"],
mandatory = False,
doc = "Internal API diagrams (class diagrams). " +
"Classified separately so their FlatBuffers outputs are exposed via " +
"ArchitecturalDesignInfo.internal_api for downstream validation.",
),
"maturity": attr.string(
default = "release",
values = ["release", "development"],
doc = "Maturity level of the architectural design. 'release' treats validation findings as errors; 'development' emits warnings and continues.",
),
"_puml_parser": attr.label(
default = Label("@score_tooling//plantuml/parser:parser"),
executable = True,
cfg = "exec",
doc = "PlantUML parser tool that generates FlatBuffers from .puml files",
),
"_linker": attr.label(
default = Label("@score_tooling//plantuml/parser:linker"),
executable = True,
cfg = "exec",
doc = "Tool that generates plantuml_links.json from FlatBuffers diagram outputs",
),
"_puml_rst_template": attr.label(
default = Label("//bazel/rules/rules_score:templates/puml_diagram.template.rst"),
allow_single_file = True,
doc = "RST template for PlantUML diagram wrapper pages.",
),
}
attrs.update(VALIDATION_ATTRS)
attrs.update(VERBOSITY_ATTR)
return attrs

_architectural_design = rule(
implementation = _architectural_design_impl,
doc = "Collects architectural design documents and diagrams for S-CORE process compliance. " +
"Automatically parses PlantUML files to produce FlatBuffers binary representations.",
attrs = _architectural_design_attrs(),
attrs = dict(
{
"static": attr.label_list(
allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"],
mandatory = False,
doc = "Static architecture diagrams (class diagrams, component diagrams, etc.)",
),
"dynamic": attr.label_list(
allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"],
mandatory = False,
doc = "Dynamic architecture diagrams (sequence diagrams, activity diagrams, etc.)",
),
"public_api": attr.label_list(
allow_files = [".puml", ".plantuml"],
mandatory = False,
doc = "Public API diagrams (parsed identically to static/dynamic). " +
"Classified separately so their lobster items are exposed via " +
"public_api_lobster_files, enabling failure-mode-to-interface " +
"traceability at the dependable element level.",
),
"internal_api": attr.label_list(
allow_files = [".puml", ".plantuml"],
mandatory = False,
doc = "Internal API diagrams (class diagrams). " +
"Classified separately so their FlatBuffers outputs are exposed via " +
"ArchitecturalDesignInfo.internal_api for downstream validation.",
),
"maturity": attr.string(
default = "release",
values = ["release", "development"],
doc = "Maturity level of the architectural design. 'release' treats validation findings as errors; 'development' emits warnings and continues.",
),
"_puml_parser": attr.label(
default = Label("@score_tooling//plantuml/parser:puml_cli"),
executable = True,
cfg = "exec",
doc = "PlantUML parser tool that generates FlatBuffers from .puml files",
),
"_puml_rst_template": attr.label(
default = Label("//bazel/rules/rules_score:templates/puml_diagram.template.rst"),
allow_single_file = True,
doc = "RST template for PlantUML diagram wrapper pages.",
),
},
**dict(VALIDATION_ATTRS, **VERBOSITY_ATTR)
),
)

# ============================================================================
Expand Down
Loading
Loading