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,7 +90,8 @@ 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 `.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
11 changes: 6 additions & 5 deletions bazel/rules/rules_score/docs/tooling_architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,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.
* - **safety_analysis_tools**
- ``//bazel/rules/rules_score:safety_analysis_tools``
(``src/safety_analysis_tools.py``, local)
Expand Down
85 changes: 38 additions & 47 deletions bazel/rules/rules_score/private/architectural_design.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,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 @@ -52,25 +56,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 @@ -80,16 +91,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 _architectural_design_impl(ctx):
"""Implementation for architectural_design rule.
Expand All @@ -110,46 +123,26 @@ 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],
transitive = [all_source_files, all_idmaps],
)

# Generate a thin RST wrapper for every .puml diagram so it appears as a
Expand All @@ -164,15 +157,19 @@ def _architectural_design_impl(ctx):
sphinx_srcs = depset(rst_wrappers, transitive = [sphinx_files])

return [
DefaultInfo(files = all_source_files),
# Include idmaps alongside source files so that plain sphinx_module
# consumers (without dependable_element) can pick up idmap sidecars
# simply by adding this target to their srcs.
DefaultInfo(files = depset(transitive = [all_source_files, all_idmaps])),
ArchitecturalDesignInfo(
static = static_fbs,
dynamic = dynamic_fbs,
internal_api = internal_api_fbs,
name = ctx.label.name,
public_api_lobster_files = public_api_lobster,
),
# Source diagram files + plantuml_links.json for the sphinx documentation build
# Source diagram files + .idmap.json sidecars for the sphinx documentation build.
# The clickable_plantuml extension reads *.idmap.json to resolve cross-diagram links.
SphinxSourcesInfo(
srcs = sphinx_srcs,
deps = sphinx_srcs,
Expand Down Expand Up @@ -219,13 +216,7 @@ _architectural_design = rule(
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",
doc = "PlantUML parser tool that generates FlatBuffers and .idmap.json from .puml files",
),
"_puml_rst_template": attr.label(
default = Label("//bazel/rules/rules_score:templates/puml_diagram.template.rst"),
Expand Down
12 changes: 0 additions & 12 deletions plantuml/linker/README.md

This file was deleted.

Loading
Loading