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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# CHANGELOG


## v8.0.0-rc.1 (2026-03-10)

### Bug Fixes

- Update poetry lock
([`93ceffb`](https://github.com/avengineers/spl-core/commit/93ceffba6ff3ba6ac0c3dd4e09dec2d277f72a5b))

### Features

- Refactor report generation
([`ab8baab`](https://github.com/avengineers/spl-core/commit/ab8baaba94139f16be1bddce84b0e48a13631fdf))

- Update sphinx-needs
([`a2e1957`](https://github.com/avengineers/spl-core/commit/a2e195796c96cc63bd43cc3db7c8f190595990ad))


## v7.17.0 (2026-03-04)

### Features
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
project = "SPL Core"
copyright = "2026, RMT"
author = "RMT"
release = "7.17.0"
release = "8.0.0-rc.1"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
2,523 changes: 1,278 additions & 1,245 deletions poetry.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "spl-core"
version = "7.17.0"
version = "8.0.0-rc.1"
description = "Software Product Line Support for CMake"
authors = ["Avengineers <karsten.guenther@kamg.de>"]
license = "MIT"
Expand Down Expand Up @@ -36,7 +36,7 @@ doxysphinx = "^3.3"
sphinx = "^7.3"
sphinx-rtd-theme = "^2.0"
sphinxcontrib-mermaid = "^0.9"
sphinx-needs = "^2.0"
sphinx-needs = "^7.0"
sphinx-test-reports = "^1.0"
sphinx-rtd-size = "^0.2"
sphinxcontrib-datatemplates = "^0.11"
Expand Down Expand Up @@ -64,6 +64,7 @@ loguru = "*"
flake8 = "*"
pipenv = "*"
python-semantic-release = "^9.16.1"
beautifulsoup4 = "*"

[tool.semantic_release]
version_toml = ["pyproject.toml:tool.poetry.version"]
Expand Down
2 changes: 1 addition & 1 deletion src/spl_core/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "7.17.0"
__version__ = "8.0.0-rc.1"
150 changes: 22 additions & 128 deletions src/spl_core/kickstart/templates/project/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import json
import os
import datetime
import re

from spl_core.report_generation.spl_sphinx import SplSphinx
from spl_core.report_generation.spl_html_settings import html_theme, html_show_sourcelink, html_theme_options # noqa: F401

day = datetime.date.today()
# meta data #################################################################
Expand Down Expand Up @@ -42,136 +44,28 @@
# Omit "documentation" in title
html_title = f"{project} {release}"

html_theme = "sphinx_rtd_theme"

# Show hyper link which leeds to the source of page displayed
html_show_sourcelink = True

html_theme_options = {
"canonical_url": "",
"analytics_id": "", # Provided by Google in your dashboard
"display_version": True,
"prev_next_buttons_location": "bottom",
"style_external_links": True,
"logo_only": False,
"style_nav_header_background": "white",
# Toc options
"collapse_navigation": True,
"sticky_navigation": True,
"navigation_depth": 6,
"includehidden": True,
"titles_only": False,
}

# EXTENSIONS AND THEIR CONFIGS ##############################################
# Use default SPL HTML theme configuration (imported from spl_html_settings)
# Can be overridden after import if needed

# extensions and their configuration #########################################
extensions = []

extensions.append("sphinx_rtd_size")
sphinx_rtd_size_width = "90%"

extensions.append("sphinxcontrib.mermaid")

extensions.append("sphinx_needs")

extensions.append("sphinxcontrib.test_reports")
tr_report_template = "doc/test_report_template.txt"


def tr_link(app, need, needs, first_option_name, second_option_name, *args, **kwargs):
"""Make links between 'needs'. In comparison to the default 'tr_link' function,
this function supports regular expression pattern matching."""
if first_option_name not in need:
return ""
# Get the value of the 'first_option_name'
first_option_value = need[first_option_name]

links = []
for need_target in needs.values():
# Skip linking to itself
if need_target["id"] == need["id"]:
continue
if second_option_name not in need_target:
continue

if first_option_value is not None and len(first_option_value) > 0:
second_option_value = need_target[second_option_name]
if second_option_value is not None and len(second_option_value) > 0:
if first_option_value == second_option_value:
links.append(need_target["id"])
# if the first option value has a *, use regex matching
elif "*" in first_option_value:
if re.match(first_option_value, second_option_value):
links.append(need_target["id"])

return links


needs_functions = [tr_link]

extensions.append("sphinx.ext.todo")

# Render Your Data Readable ##################################################
# Enables adding Jupyter notebooks to toctree
# @see https://sphinxcontribdatatemplates.readthedocs.io/en/latest/index.html
extensions.append("sphinxcontrib.datatemplates")

# needs_types - this option allows the setup of own need types like bugs, user_stories and more.
needs_types = [
dict(directive="req", title="Requirement", prefix="R_", color="#BFD8D2", style="node"),
dict(
directive="spec",
title="Specification",
prefix="S_",
color="#FEDCD2",
style="node",
),
dict(
directive="impl",
title="Implementation",
prefix="I_",
color="#DF744A",
style="node",
),
dict(directive="test", title="Test Case", prefix="T_", color="#DCB239", style="node"),
]

# Define own options
needs_extra_options = ["integrity"]

# Define own link types
needs_extra_links = [
# SWE.3 BP.5: link from Implementation (Software unit) to Specification (Software detailed design)
# AND
# SWE.2 BP.7: link from Requirements (Software Requirement) to Architecture (Software Architecture)
{"option": "implements", "incoming": "is implemented by", "outgoing": "implements"},
# SWE.4 BP.5: link from Test Case (Unit test specification) to Specification (Software detailed design)
{"option": "tests", "incoming": "is tested by", "outgoing": "tests"},
# SWE.4 BP.5: link from Test Case (Unit test specification) to Test Result (Unit test result)
{"option": "results", "incoming": "is resulted from", "outgoing": "results"},
]

# Link tests results to the test cases
needs_global_options = {
"results": "[[tr_link('title', 'case')]]",
}

# Parse markdown files
extensions.append("myst_parser")
myst_enable_extensions = [
"colon_fence",
"deflist",
"html_admonition",
"html_image",
]

# The suffix of source filenames.
source_suffix = [
".rst",
".md",
]
# EXTENSIONS AND THEIR CONFIGS ##############################################

# Get default SPL extensions and their configurations
extensions = SplSphinx.default_extensions
extension_configs = SplSphinx.default_extension_configs

# Apply extension-specific configurations
sphinx_rtd_size_width = extension_configs["sphinx_rtd_size_width"]
tr_report_template = extension_configs["tr_report_template"]
myst_enable_extensions = extension_configs["myst_enable_extensions"]
source_suffix = extension_configs["source_suffix"]

# Import default SPL sphinx-needs configuration
needs_from_toml = ".venv/Lib/site-packages/spl_core/report_generation/ubproject.toml"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a Windows only path (Lib/site-packages). On Linux/macOS it would be lib/pythonX.Y/site-packages. It should be resolved dynamically, e.g.:

from importlib.resources import files
needs_from_toml = str(files("spl_core.report_generation").joinpath("ubproject.toml"))

# Additional import required because the configuration references custom functions defined in this module
needs_functions = SplSphinx.default_needs_functions
needs_global_options = SplSphinx.default_needs_global_options

# Provide all config values to jinja
html_context = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The html_context is updated based on files known to spl-core (autoconf and sphinx build config).
Because these files and the environment variables are generated by spl-core it makes sense to move this logic to the spl-core sphinx report generation module.
See a similar example here: https://github.com/cuinixam/SPLed/blob/33524bc316be5682618ed90523be4d5883c6f1b8/conf.py#L45

"build_config": {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ Passed test cases

.. needtable::
:filter: '{id}' in tags and type == '{case_need}' and 'passed' == result
:columns: id, title, is resulted from
:columns: id, title, results from
:style: table

Failed test cases
-----------------

.. needtable::
:filter: '{id}' in tags and type == '{case_need}' and 'failure' == result
:columns: id, title, is resulted from
:columns: id, title, results from
:style: table

Skipped test cases
------------------

.. needtable::
:filter: '{id}' in tags and type == '{case_need}' and 'skipped' == result
:columns: id, title, is resulted from
:columns: id, title, results from
:style: table

Statistics
Expand Down
Empty file.
23 changes: 23 additions & 0 deletions src/spl_core/report_generation/spl_html_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""HTML theme configuration for SPL projects."""

# HTML Theme
html_theme = "sphinx_rtd_theme"

# Show source links
html_show_sourcelink = True

# Read the Docs Theme Options
html_theme_options = {
"canonical_url": "",
"analytics_id": "",
"display_version": True,
"prev_next_buttons_location": "bottom",
"style_external_links": True,
"logo_only": False,
"style_nav_header_background": "white",
"collapse_navigation": True,
"sticky_navigation": True,
"navigation_depth": 6,
"includehidden": True,
"titles_only": False,
}
98 changes: 98 additions & 0 deletions src/spl_core/report_generation/spl_sphinx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""Centralized Sphinx configuration for SPL projects."""

import re
from typing import Any, ClassVar

from sphinx.application import Sphinx
from sphinx_needs.data import NeedsMutable, NeedsView
from sphinx_needs.need_item import NeedItem, NeedPartItem


def sple_tr_link(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no way to keep the name of this method tr_link and use it to override the default sphinx needs tr_link implementation?
If we change the name, one must change in all test files in all SPLs from tr_link to sple_tr_link.

app: Sphinx,
need: NeedItem | NeedPartItem,
needs: NeedsView | NeedsMutable,
first_option_name: str,
second_option_name: str,
*args: Any,
**kwargs: Any,
) -> list[str]:
"""Make links between 'needs'. In comparison to the default 'tr_link' function,
this function supports regular expression pattern matching."""
if first_option_name not in need:
return []
# Get the value of the 'first_option_name'
first_option_value = need[first_option_name]

links = []
for need_target in needs.values():
# Skip linking to itself
if need_target["id"] == need["id"]:
continue
if second_option_name not in need_target:
continue

if first_option_value is not None and len(first_option_value) > 0:
second_option_value = need_target[second_option_name]
if second_option_value is not None and len(second_option_value) > 0:
if first_option_value == second_option_value:
links.append(need_target["id"])
# if the first option value has a *, use regex matching
elif "*" in first_option_value:
if re.match(first_option_value, second_option_value):
links.append(need_target["id"])

return links


class SplSphinx:
"""Centralized Sphinx configuration for SPL projects.

This class provides default configurations for sphinx-needs extension,
including custom need types, link types, and linking functions.

All configurations are available as class properties:
- default_needs_types: Need type configurations (req, spec, impl, test)
- default_needs_extra_options: Additional need options
- default_needs_extra_links: Link type configurations for traceability
- default_needs_global_options: Global options for needs
- default_needs_functions: Custom needs functions
- default_extensions: List of Sphinx extensions
- default_extension_configs: Extension-specific configurations
"""

# Needs Global Options - automatically links test results to test cases
# sphinx-needs 6.x requires FieldDefault format: {"default": ...}
default_needs_global_options: ClassVar[dict[str, dict[str, str]]] = {
"results": {"default": "[[sple_tr_link('title', 'case')]]"},
}

# Needs Functions - custom linking functions
default_needs_functions: ClassVar[list[Any]] = [sple_tr_link]

# Default Sphinx Extensions
default_extensions: ClassVar[list[str]] = [
"sphinx_rtd_size",
"sphinxcontrib.mermaid",
"sphinx_needs",
"sphinxcontrib.test_reports",
"sphinx.ext.todo",
"sphinxcontrib.datatemplates",
"myst_parser",
]

# Extension-specific configurations
default_extension_configs: ClassVar[dict[str, str | list[str]]] = {
"sphinx_rtd_size_width": "90%",
"tr_report_template": "doc/test_report_template.txt",
"myst_enable_extensions": [
"colon_fence",
"deflist",
"html_admonition",
"html_image",
],
"source_suffix": [
".rst",
".md",
],
}
Loading