Skip to content

Add SysML / Gaphor diagrams under sphinx-needs items (prototype)#50

Open
ubmarco wants to merge 2 commits into
mainfrom
feat/sysml-gaphor-integration
Open

Add SysML / Gaphor diagrams under sphinx-needs items (prototype)#50
ubmarco wants to merge 2 commits into
mainfrom
feat/sysml-gaphor-integration

Conversation

@ubmarco
Copy link
Copy Markdown
Member

@ubmarco ubmarco commented May 5, 2026

Summary

Demonstrates that .. diagram:: from gaphor.extensions.sphinx works the same way as .. uml:: does today — it can be embedded directly inside a sphinx-needs arch:: item and renders to SVG + PDF on every build. Prototype intended for a customer SysML 1.5 / 1.6 showcase.

What this wires up

  • gaphor>=3.3.2 as a dependency and gaphor.extensions.sphinx in docs/conf.py with a gaphor_models mapping.
  • Two model files under docs/automotive-adas/sysml/:
    • adas-tsr-bdd.gaphor — generated by author_adas_bdd.py (Gaphor Python API). A Block Definition Diagram for TrafficSignRecognition decomposed into FrontCamera / SignInterpreter / VehicleControl.
    • sysml-car.gaphor — Gaphor's stock SysML example, dropped in unchanged to prove a third-party .gaphor file works as-is.
  • A diagram embedded inside ARCH_007 in sys_3_sys_arch.rst, alongside the existing PlantUML view.
  • .readthedocs.yaml apt packages required by PyGObject + Cairo (libgirepository, libcairo2-dev, pkg-config, python3-dev, gir1.2-pango-1.0).
  • A new automotive-adas/sysml/index.rst page that explains the integration, the authoring workflow, and the SysML 1.5 XMI conversion question.

Non-obvious fix

Installing Gaphor pulls in PyGObject. With PyGObject available, matplotlib auto-selects the gtk4agg backend, which then hangs the build at "writing output… [4%]" on any headless host (CI, RTD, sandboxes) because GDK fails to open a surface. Pinned matplotlib.use(\"Agg\") early in conf.py before any extension imports it. Without this the build hangs silently.

On SysML 1.5 / Gaphor

  • Gaphor implements SysML 1.6, the minor revision of 1.5 that retains the same core constructs (Block, Property, Port, Connector, Requirement) — the right fit for a SysML 1.5 customer showcase.
  • The .gaphor format is XML but is not XMI: different namespace (https://gaphor.org/model), different schema, and it bundles per-diagram presentation/layout that XMI lacks.
  • Gaphor removed its XMI export in 3.1.0 (Remove XMI export plugin gaphor/gaphor#3444); it was declared under-tested. There is no off-the-shelf XMI ↔ .gaphor converter.
  • Realistic paths for a project that already has SysML 1.5 XMI: (a) re-author in Gaphor (GUI or the Python script pattern in this PR), or (b) write a small custom converter for whatever XMI subset the customer actually uses. Both options are written up in the new SysML page.

Test plan

  • uv run sphinx-build -W -b html docs docs/_build/html succeeds (warnings-as-errors, matching RTD's fail_on_warning: true).
  • Both diagrams render to SVG + PDF under docs/_build/html/gaphor/ and the SVGs are picked up under _images/.
  • ARCH_007 in sys_3_sys_arch.html shows the BDD figure inside the need's content cell.
  • New sysml/index.html shows the sysml-car figure rendered from an unmodified Gaphor example file.
  • RTD build with the new apt packages (verify on the next RTD push).
  • Re-run uv run python docs/automotive-adas/sysml/author_adas_bdd.py — should regenerate adas-tsr-bdd.gaphor deterministically.

Notes for review

  • adas-tsr-bdd.gaphor is text/XML and tracked as code rather than a binary artifact. Re-run the authoring script after any structural change.
  • Gaphor is a heavy dependency (pulls in PyGObject + pycairo). Acceptable for the demo; for a slimmer prod docs build we could split the SysML pages into an opt-in toctree.
  • This is a prototype — the BDD is intentionally small. Easy to extend with Ports, Connectors and Requirement allocations once the integration shape is approved.

ubmarco added 2 commits May 5, 2026 11:14
Demonstrates that ``.. diagram::`` from gaphor.extensions.sphinx works
the same way as ``.. uml::`` does today: it can be embedded directly
inside a sphinx-needs ``arch::`` item and renders to SVG/PDF on every
build.

Wires up:
- gaphor>=3.3.2 as a dependency and ``gaphor.extensions.sphinx`` in
  ``conf.py`` with a ``gaphor_models`` mapping.
- Two model files under ``docs/automotive-adas/sysml/``:
  ``adas-tsr-bdd.gaphor`` (regenerated by ``author_adas_bdd.py``) and
  Gaphor's stock ``sysml-car.gaphor`` example.
- A diagram embedded under ARCH_007 (Traffic Sign Recognition) next to
  the existing PlantUML view.
- A new ``sysml/index.rst`` page that explains the integration, the
  authoring workflow (GUI vs Python API) and why dropping in raw SysML
  1.5 XMI is not viable today (Gaphor removed XMI export in 3.1.0; the
  ``.gaphor`` schema differs from XMI).
- Read-the-Docs apt packages required by PyGObject + Cairo.
- ``matplotlib.use("Agg")`` early in ``conf.py`` to keep matplotlib off
  the GTK4 backend that PyGObject would otherwise default to (the cause
  of a build hang at "writing output... [4%]" on headless hosts).

Notes for review:
- Gaphor implements SysML 1.6, the minor revision of 1.5 that retains
  the same core constructs (Block, Property, Port, Connector,
  Requirement); for a "SysML 1.5" customer showcase this is the right
  fit.
- ``adas-tsr-bdd.gaphor`` is text/XML and tracked as code; rerun
  ``uv run python docs/automotive-adas/sysml/author_adas_bdd.py`` to
  regenerate after structural changes.
Addresses two review issues:

1. Cairo / PyGObject build failure on Read-the-Docs. Gaphor's Sphinx
   extension needs Cairo + GTK girepository at build time, which
   require a C toolchain. They are now decoupled from the doc build:
   - ``gaphor`` moves to a ``[render]`` optional extra; the doc build
     only sees the ordinary Sphinx + sphinx-needs stack.
   - ``gaphor.extensions.sphinx`` is dropped from ``conf.py``; the
     ``gaphor_models`` mapping is removed.
   - The Gaphor-related ``apt_packages`` are removed from
     ``.readthedocs.yaml``.
   - SysML diagrams are now ordinary committed SVGs embedded with
     ``.. figure::``.

2. BDD layout looked broken. The previous Python authoring placed
   ``PropertyItem`` parts at hard-coded offsets that overlapped the
   parent block's name and ``«block»`` stereotype. The script now
   sets ``BlockItem.show_parts = 1`` so Gaphor renders parts inside
   the parent's "parts" compartment, and arranges the parent + three
   sub-blocks on a clean two-row grid. The committed
   ``adas-tsr-bdd__tsr-block-definition.svg`` reflects the new layout.

New rendering workflow (see ``docs/automotive-adas/sysml/index.rst``):

  uv sync --extra render
  uv run python docs/automotive-adas/sysml/render_sysml.py

The render script walks every ``.gaphor`` file in the directory and
writes one SVG per diagram. Run on a developer machine that has Cairo /
GTK system libraries; commit the regenerated SVGs. CI / RTD do not run
this step.

The matplotlib ``Agg`` backend pin in ``conf.py`` stays as a defensive
guard for developers who install the ``[render]`` extra in the same
venv they build docs from — without it, the presence of PyGObject
flips matplotlib to the GTK4 backend and the build hangs at
"writing output... [4%]".

For real SysML 1.5 XMI files from Cameo / Papyrus / MagicDraw / Rhapsody,
the recommended path is now to export individual diagrams to SVG from
the source tool (all of them support this out of the box), commit the
SVG, and reference it the same way as the Gaphor-rendered example. No
``.gaphor`` conversion needed.
@ubmarco
Copy link
Copy Markdown
Member Author

ubmarco commented May 5, 2026

Updated to address the two issues:

1. Cairo / Gaphor build dependency removed. The doc build no longer touches Gaphor at all:

  • gaphor moved to a [render] optional extra in pyproject.toml.
  • gaphor.extensions.sphinx removed from conf.py; gaphor_models removed.
  • The Gaphor-related apt_packages (libgirepository, libcairo2-dev, ...) removed from .readthedocs.yaml.
  • SysML diagrams are now ordinary committed SVGs embedded via plain .. figure::.

2. BDD layout fixed. The previous Python authoring placed PropertyItem parts at hard-coded offsets that overlapped the parent block's name and «block» stereotype. The author script now sets BlockItem.show_parts = 1 so Gaphor renders parts inside the parent's "parts" compartment, and arranges the parent block + three sub-blocks on a clean two-row grid. The committed SVG reflects the new layout.

New rendering workflow (documented in docs/automotive-adas/sysml/index.rst):

uv sync --extra render
uv run python docs/automotive-adas/sysml/render_sysml.py

Walks every .gaphor file in the directory and writes one SVG per diagram. Run on a developer machine that has the Cairo / GTK system libraries; commit the regenerated SVGs. CI and RTD do not run this step.

For real SysML 1.5 XMI (Cameo / Papyrus / MagicDraw / Rhapsody), the recommended path is now: export individual diagrams to SVG from the source tool (all of them support this out of the box), commit the SVG, reference it via .. figure::. No .gaphor conversion needed; this works with any SysML-capable tool.

Other options I explored before settling on this: PlantUML stereotype mimicry (text-only but not real SysML), Kroki online rendering (no native SysML support), Eclipse Papyrus headless export (heavyweight Java/Eclipse install), Drawio CLI (basic SysML stencils only). The "pre-render and commit" approach was the only one that meets both "no compiler on the user machine" and "ingests real SysML 1.5".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant