diff --git a/BUILD b/BUILD index 8146d9b8..41430e46 100644 --- a/BUILD +++ b/BUILD @@ -37,6 +37,7 @@ copyright_checker( # Add other directories/files you want to check ], config = "//cr_checker/resources:config", + exclusion = "//cr_checker/resources:exclusion", template = "//cr_checker/resources:templates", visibility = ["//visibility:public"], ) diff --git a/bazel/rules/rules_score/BUILD b/bazel/rules/rules_score/BUILD index 4a50d759..d00047b4 100644 --- a/bazel/rules/rules_score/BUILD +++ b/bazel/rules/rules_score/BUILD @@ -109,6 +109,7 @@ sphinx_module( [ "docs/**/*.rst", "docs/**/*.puml", + "docs/**/*.md", ], allow_empty = True, ), diff --git a/bazel/rules/rules_score/docs/_assets/MySeooc_FTA.puml b/bazel/rules/rules_score/docs/_assets/MySeooc_FTA.puml new file mode 100644 index 00000000..596187fc --- /dev/null +++ b/bazel/rules/rules_score/docs/_assets/MySeooc_FTA.puml @@ -0,0 +1,25 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml MySeooc_FTA + +!include fta_metamodel.puml + +$TopEvent("KVS returns stale data after power loss", "MySeooc.FM_001") + +$OrGate("OG_1", "MySeooc.FM_001") + +$BasicEvent("Write not flushed to storage", "MySeooc.RC_001", "OG_1") +$BasicEvent("Corruption on unclean shutdown", "MySeooc.RC_002", "OG_1") + +@enduml diff --git a/bazel/rules/rules_score/docs/_assets/MySeooc_PublicApi.puml b/bazel/rules/rules_score/docs/_assets/MySeooc_PublicApi.puml new file mode 100644 index 00000000..77453a03 --- /dev/null +++ b/bazel/rules/rules_score/docs/_assets/MySeooc_PublicApi.puml @@ -0,0 +1,21 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml MySeooc_PublicApi + +interface "KeyValueStore" as KVS { + + write(key: string, value: bytes): Result + + read(key: string): Optional +} + +@enduml diff --git a/bazel/rules/rules_score/docs/_assets/MySeooc_StaticDesign.puml b/bazel/rules/rules_score/docs/_assets/MySeooc_StaticDesign.puml new file mode 100644 index 00000000..87e3df99 --- /dev/null +++ b/bazel/rules/rules_score/docs/_assets/MySeooc_StaticDesign.puml @@ -0,0 +1,23 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml MySeooc_StaticDesign + +package "MySeooc" as MySeooc <> { + component "KvsComponent" as KvsComponent <> { + component "KeyValueStore" as KeyValueStore <> + component "StorageBackend" as StorageBackend <> + } +} + +@enduml diff --git a/bazel/rules/rules_score/docs/_assets/MySeooc_WriteSequence.puml b/bazel/rules/rules_score/docs/_assets/MySeooc_WriteSequence.puml new file mode 100644 index 00000000..578dd90a --- /dev/null +++ b/bazel/rules/rules_score/docs/_assets/MySeooc_WriteSequence.puml @@ -0,0 +1,25 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml MySeooc_WriteSequence + +actor Caller +participant KeyValueStore +participant StorageBackend + +Caller -> KeyValueStore : write(key, value) +KeyValueStore -> StorageBackend : flush() +StorageBackend --> KeyValueStore : OK +KeyValueStore --> Caller : Result::Ok + +@enduml diff --git a/bazel/rules/rules_score/docs/_assets/fta_metamodel.puml b/bazel/rules/rules_score/docs/_assets/fta_metamodel.puml new file mode 100644 index 00000000..770f7a54 --- /dev/null +++ b/bazel/rules/rules_score/docs/_assets/fta_metamodel.puml @@ -0,0 +1,59 @@ +' ******************************************************************************* +' Copyright (c) 2025 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +' AND gate: flat bottom + elliptic arch on top +sprite $and + + + +' OR gate: pointed top, bowed sides, concave bottom +sprite $or + + + +' Transfer-In gate: upward triangle +sprite $transferin + + + +'skinparam linetype polyline +'skinparam linetype ortho + +!procedure $TopEvent($name, $alias) + rectangle "$name" as $alias +!endprocedure + +!procedure $IntermediateEvent($name, $alias, $connection) + rectangle "$name" as $alias + $alias -u-> $connection +!endprocedure + +!procedure $BasicEvent($name, $alias, $connection) + usecase "$name" as $alias + $alias -u-> $connection +!endprocedure + +!procedure $AndGate($alias, $connection) + rectangle " " <<$and>> as $alias + $alias -u-> $connection +!endprocedure + +!procedure $OrGate($alias, $connection) + rectangle " " <<$or>> as $alias + $alias -u-> $connection +!endprocedure + +!procedure $TransferInGate($alias, $connection) + rectangle " " <<$transferin>> as $alias + $alias -u-> $connection +!endprocedure diff --git a/bazel/rules/rules_score/docs/rules_score_overview.puml b/bazel/rules/rules_score/docs/_assets/rules_score_overview.puml similarity index 100% rename from bazel/rules/rules_score/docs/rules_score_overview.puml rename to bazel/rules/rules_score/docs/_assets/rules_score_overview.puml diff --git a/bazel/rules/rules_score/docs/_assets/seooc_flow.puml b/bazel/rules/rules_score/docs/_assets/seooc_flow.puml new file mode 100644 index 00000000..ba783425 --- /dev/null +++ b/bazel/rules/rules_score/docs/_assets/seooc_flow.puml @@ -0,0 +1,89 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml seooc_flow + +skinparam linetype ortho +skinparam defaultTextAlignment center +skinparam ArrowFontSize 11 +skinparam ArrowFontStyle italic + +skinparam rectangle { + BackgroundColor<> #EFF6FB + BorderColor<> #0066B1 + BackgroundColor<> #FFF8E1 + BorderColor<> #F9A825 + BackgroundColor<> #E8F5E9 + BorderColor<> #388E3C +} + +' ── Inputs ────────────────────────────────────────────────────────────────── +rectangle "System Requirements\n(.trlc)" <> as asr_in +rectangle "Feature Requirements\n(.trlc)" <> as feat_in +rectangle "Component Requirements\n(.trlc)" <> as comp_in +rectangle "Assumptions of Use\n(.trlc)" <> as aou_in +rectangle "Architecture Diagrams\n(.puml / .svg)" <> as arch_in +rectangle "Unit Design Diagrams\n(.puml / .rst)" <> as ud_in +rectangle "Implementation\n(cc_library / py_library…)" <> as impl_in +rectangle "Tests\n(cc_test / py_test…)" <> as tests_in +rectangle "Failure Modes\n(.trlc)" <> as fm_in +rectangle "Control Measures\n(.trlc)" <> as cm_in +rectangle "FTA Diagrams\n(.puml)" <> as fta_in + +' ── Bazel Rules ───────────────────────────────────────────────────────────── +rectangle "assumed_system_requirements" <> as asr_r +rectangle "feature_requirements" <> as feat_r +rectangle "component_requirements" <> as comp_r +rectangle "assumptions_of_use" <> as aou_r +rectangle "architectural_design" <> as arch_r +rectangle "unit_design" <> as ud_r +rectangle "unit" <> as unit_r +rectangle "component" <> as comp_r2 +rectangle "fmea" <> as fmea_r +rectangle "dependability_analysis" <> as da_r +rectangle "dependable_element" <> as de_r + +' ── Outputs ────────────────────────────────────────────────────────────────── +rectangle "HTML Documentation\n+ Traceability Report" <> as out + +' ── Connections ────────────────────────────────────────────────────────────── +asr_in --> asr_r +feat_in --> feat_r +comp_in --> comp_r +aou_in --> aou_r +arch_in --> arch_r +ud_in --> ud_r +impl_in --> unit_r +tests_in --> unit_r +fm_in --> fmea_r +cm_in --> fmea_r +fta_in --> fmea_r + +asr_r --> feat_r : deps +asr_r --> aou_r +feat_r --> aou_r : requirements +feat_r --> de_r +comp_r --> comp_r2 : requirements + +ud_r --> unit_r : unit_design +unit_r --> comp_r2 : components +fmea_r --> da_r : fmea + +arch_r --> de_r : architectural_design +aou_r --> de_r : assumptions_of_use +comp_r2 --> de_r : components +da_r --> de_r : dependability_analysis + +de_r --> out + +@enduml diff --git a/bazel/rules/rules_score/docs/index.rst b/bazel/rules/rules_score/docs/index.rst index 29993831..46b06d7d 100644 --- a/bazel/rules/rules_score/docs/index.rst +++ b/bazel/rules/rules_score/docs/index.rst @@ -11,405 +11,19 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* + SCORE Rules for Bazel ===================== -This package provides Bazel build rules for defining and building SCORE documentation modules with integrated Sphinx-based HTML generation. - -.. contents:: Table of Contents - :depth: 2 - :local: - - -Overview --------- - -The ``rules_score`` package provides Bazel rules for structuring and documenting safety-critical software following S-CORE process guidelines: - -**Documentation Rule:** - -- ``sphinx_module``: Generic rule for building Sphinx HTML documentation with dependency support - -**Artifact Rules:** - -- ``feature_requirements``: High-level feature specifications -- ``component_requirements``: Component-level requirements -- ``assumptions_of_use``: Safety-relevant operating conditions -- ``architectural_design``: Software architecture documentation -- ``safety_analysis``: Detailed safety analysis (FMEA, FTA) -- ``dependability_analysis``: Comprehensive safety analysis results - -**Structural Rules:** - -- ``unit``: Smallest testable software element (design + implementation + tests) -- ``component``: Collection of units providing specific functionality -- ``dependable_element``: Complete Safety Element out of Context (SEooC) with full documentation - -All rules support cross-module dependencies for automatic sphinx-needs integration and HTML merging. - - -Toolchain Configuration ------------------------ - -The ``sphinx_toolchain`` rule allows you to configure the Sphinx build environment with custom extensions. External modules must define and register their own toolchain to use ``rules_score``. - -**Setting Up Toolchain in External Module** - -In your MODULE.bazel: - -.. code-block:: python - - # Add rules_score dependency - bazel_dep(name = "score_tooling", version = "1.3.2") - - # Add dependencies for custom Sphinx extensions (if needed) - bazel_dep(name = "score_docs_as_code", version = "3.0.1") - - # Register your custom toolchain - register_toolchains("//:my_toolchain") - -In your BUILD file: - -.. code-block:: python - - load("@aspect_rules_py//py:defs.bzl", "py_binary") - load("@score_tooling//bazel/rules/rules_score:sphinx_toolchain.bzl", "sphinx_toolchain") - - # Define Sphinx binary with your required extensions - py_binary( - name = "score_build", - srcs = ["@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py"], - main = "@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py", - visibility = ["//visibility:public"], - deps = [ - "@score_tooling//bazel/rules/rules_score:sphinx_module_ext", - "@score_docs_as_code//src:plantuml_for_python", - "@score_docs_as_code//src/extensions/score_sphinx_bundle", - # Add your custom Sphinx extensions here - ], - ) - - # Create toolchain instance - sphinx_toolchain( - name = "score_sphinx_toolchain", - sphinx = ":score_build", - ) - - # Register as Bazel toolchain - toolchain( - name = "my_toolchain", - exec_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], - target_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], - toolchain = ":score_sphinx_toolchain", - toolchain_type = "@score_tooling//bazel/rules/rules_score:toolchain_type", - visibility = ["//visibility:public"], - ) - -**sphinx_toolchain Parameters:** - -- ``sphinx``: Label to Sphinx build binary (mandatory) -- ``conf_template``: Label to conf.py template file (optional, default: ``@score_tooling//bazel/rules/rules_score:templates/conf.template.py``) -- ``html_merge_tool``: Label to HTML merge tool (optional, default: ``@score_tooling//bazel/rules/rules_score:sphinx_html_merge``) - - -sphinx_module -------------- - -Builds Sphinx-based HTML documentation from RST source files with support for dependencies and cross-referencing. - -.. code-block:: python - - sphinx_module( - name = "my_docs", - srcs = glob(["docs/**/*.rst"]), - index = "docs/index.rst", - deps = ["@external_module//:docs"], - testonly = False, - ) - -**Key Parameters:** - -- ``srcs``: RST/MD source files -- ``index``: Main index.rst file (mandatory) -- ``deps``: Other sphinx_module or dependable_element targets for cross-referencing -- ``sphinx``: Sphinx build binary (default: ``//bazel/rules/rules_score:score_build``) -- ``testonly``: If true, only testonly targets can depend on this (default: False) -- ``visibility``: Bazel visibility (default: ``["//visibility:public"]``) - -**Output:** ``/html/`` with merged dependency documentation - -**Note:** Configuration file (``conf.py``) is automatically generated from a template. - - -Dependency Management ---------------------- - -Use ``deps`` for cross-module references. HTML is automatically merged: - -.. code-block:: text - - /html/ - ├── index.html # Main documentation - ├── _static/ - ├── dependency1/ # Merged dependency - └── dependency2/ - - -Artifact Rules --------------- - -Artifact rules define S-CORE process work products. All provide ``SphinxSourcesInfo`` for documentation generation. - -**feature_requirements** - -.. code-block:: python - - feature_requirements( - name = "features", - srcs = ["docs/features.rst"], - ) - -**component_requirements** - -.. code-block:: python - - component_requirements( - name = "requirements", - srcs = ["docs/requirements.rst"], - ) - -**assumptions_of_use** - -.. code-block:: python - - assumptions_of_use( - name = "aous", - srcs = ["docs/assumptions.rst"], - feature_requirement = [":features"], - component_requirements = [":requirements"], - ) - -**architectural_design** - -.. code-block:: python - - architectural_design( - name = "architecture", - static = ["docs/static_arch.rst"], - dynamic = ["docs/dynamic_arch.rst"], - ) - -**safety_analysis** - -.. code-block:: python - - safety_analysis( - name = "safety", - controlmeasures = ["docs/controls.rst"], - failuremodes = ["docs/failures.rst"], - fta = ["docs/fta.rst"], - arch_design = ":architecture", - ) - -**dependability_analysis** - -.. code-block:: python - - dependability_analysis( - name = "analysis", - arch_design = ":architecture", - dfa = ["docs/dfa.rst"], - fmea = ["docs/fmea.rst"], - safety_analysis = [":safety"], - ) - - -Structural Rules ----------------- - -**unit** - -Define the smallest testable software element. - -.. code-block:: python - - unit( - name = "my_unit", - unit_design = [":architecture"], - implementation = ["//src:lib"], - tests = ["//tests:unit_test"], - scope = [], - testonly = True, - ) - -**Parameters:** - -- ``unit_design``: List of architectural_design targets describing the unit (mandatory) -- ``implementation``: List of implementation targets like cc_library, py_library, etc. (mandatory) -- ``tests``: List of test targets that verify the unit (mandatory) -- ``scope``: Additional targets needed for unit implementation (default: []) -- ``testonly``: If true, only testonly targets can depend on this unit (default: True) -- ``visibility``: Bazel visibility specification - -**component** - -Define a collection of units. - -.. code-block:: python - - component( - name = "my_component", - requirements = [":requirements"], - components = [":my_unit"], - tests = ["//tests:integration_test"], - testonly = True, - ) - -**Parameters:** - -- ``requirements``: List of component_requirements targets (mandatory) -- ``components``: List of unit or component targets that comprise this component (mandatory) -- ``tests``: List of component-level integration test targets (mandatory) -- ``testonly``: If true, only testonly targets can depend on this component (default: True) -- ``visibility``: Bazel visibility specification - -**dependable_element** - -Define a complete SEooC with automatic documentation generation. - -.. code-block:: python - - dependable_element( - name = "my_seooc", - description = "My safety-critical component", - assumptions_of_use = [":aous"], - requirements = [":requirements"], - architectural_design = [":architecture"], - dependability_analysis = [":analysis"], - components = [":my_component"], - tests = ["//tests:system_test"], - checklists = [], - deps = ["@platform//:platform_module"], - testonly = True, - ) - -**Parameters:** - -- ``description``: High-level description of the element (supports RST formatting) -- ``assumptions_of_use``: List of assumptions_of_use targets (mandatory) -- ``requirements``: List of requirements targets (component_requirements, feature_requirements, etc.) (mandatory) -- ``architectural_design``: List of architectural_design targets (mandatory) -- ``dependability_analysis``: List of dependability_analysis targets (mandatory) -- ``components``: List of component and/or unit targets (mandatory) -- ``tests``: List of system-level test targets (mandatory) -- ``checklists``: Optional list of safety checklist files (default: []) -- ``deps``: Optional list of other module targets for cross-referencing (default: []) -- ``testonly``: If true, only testonly targets can depend on this (default: True) -- ``visibility``: Bazel visibility specification - -**Generated Targets:** - -- ````: Sphinx module with HTML documentation -- ``_needs``: Sphinx-needs JSON for cross-referencing -- ``_index``: Generated index.rst with artifact structure - -**Implementation Details:** - -The macro automatically: - -- Generates an index.rst file with a toctree referencing all provided artifacts -- Creates symlinks to artifact files (assumptions of use, requirements, architecture, safety analysis) for co-location with the generated index -- Delegates to ``sphinx_module`` for actual Sphinx build and HTML generation -- Integrates dependencies for cross-module referencing and HTML merging - -**Overview and interrelations:** - -.. uml:: rules_score_overview.puml - :align: center - :alt: Overview of rules_score architecture - :width: 100% - -Complete Example ----------------- - -.. code-block:: python - - load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", - "architectural_design", "assumptions_of_use", - "component", "component_requirements", - "dependability_analysis", "dependable_element", - "feature_requirements", "safety_analysis", "unit") - - # Artifacts - feature_requirements(name = "features", srcs = ["docs/features.rst"]) - component_requirements(name = "reqs", srcs = ["docs/reqs.rst"]) - assumptions_of_use(name = "aous", srcs = ["docs/aous.rst"], - feature_requirement = [":features"]) - architectural_design(name = "arch", static = ["docs/arch.rst"], - dynamic = ["docs/dynamic.rst"]) - safety_analysis(name = "safety", arch_design = ":arch", - controlmeasures = ["docs/controls.rst"], - failuremodes = ["docs/failures.rst"], - fta = ["docs/fta.rst"]) - dependability_analysis(name = "analysis", arch_design = ":arch", - dfa = ["docs/dfa.rst"], - fmea = ["docs/fmea.rst"], - safety_analysis = [":safety"]) - - # Implementation - cc_library(name = "kvs_lib", srcs = ["kvs.cpp"], hdrs = ["kvs.h"]) - cc_test(name = "kvs_test", srcs = ["kvs_test.cpp"], deps = [":kvs_lib"]) - - # Structure - unit(name = "kvs_unit", unit_design = [":arch"], - implementation = [":kvs_lib"], tests = [":kvs_test"]) - component(name = "kvs_component", requirements = [":reqs"], - components = [":kvs_unit"], tests = []) - - # SEooC - dependable_element( - name = "persistency_kvs", - description = "Key-Value Store for persistent data storage", - assumptions_of_use = [":aous"], - requirements = [":reqs"], - architectural_design = [":arch"], - dependability_analysis = [":analysis"], - components = [":kvs_component"], - tests = [], - deps = ["@score_process//:score_process_module"], - ) - -Build: - -.. code-block:: bash - - bazel build //:persistency_kvs - # Output: bazel-bin/persistency_kvs/html/ - # Includes merged HTML from dependencies like score_process - -Design Rationale ----------------- - -These rules provide a structured approach to documentation by: - -1. **Two-Tier Architecture**: Generic ``sphinx_module`` for flexibility, specialized artifact rules for safety-critical work -2. **Dependency Management**: Automatic cross-referencing and HTML merging across modules -3. **Standardization**: ``dependable_element`` enforces consistent structure for safety documentation -4. **Traceability**: Sphinx-needs integration enables bidirectional traceability -5. **Automation**: Index generation, symlinking, and configuration management are automatic -6. **Build System Integration**: Bazel ensures reproducible, cacheable documentation builds - -Reference Implementation ------------------------- +``rules_score`` provides Bazel build rules for structuring and documenting +safety-critical software according to S-CORE process guidelines. It covers +the full artefact lifecycle — from requirements and architecture through +safety analysis to the top-level SEooC assembly. -See complete examples in the test BUILD file: +.. toctree:: + :maxdepth: 2 -.. literalinclude:: ../test/BUILD - :language: python - :caption: test/BUILD + overview + integration_guide + user_guide/index + rule_reference diff --git a/bazel/rules/rules_score/docs/integration_guide.rst b/bazel/rules/rules_score/docs/integration_guide.rst new file mode 100644 index 00000000..313df2f1 --- /dev/null +++ b/bazel/rules/rules_score/docs/integration_guide.rst @@ -0,0 +1,177 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Integration Guide +================= + +.. _rule-toolchain-configuration: + +Toolchain Setup +--------------- + +The ``sphinx_toolchain`` rule configures the Sphinx build environment with +custom extensions. External modules must define and register their own toolchain +to use ``rules_score``. + +**MODULE.bazel:** + +.. code-block:: python + + # Add rules_score dependency + bazel_dep(name = "score_tooling", version = "1.3.2") + + # Add dependencies for custom Sphinx extensions (if needed) + bazel_dep(name = "score_docs_as_code", version = "3.0.1") + + # Register your custom toolchain + register_toolchains("//:my_toolchain") + +**BUILD:** + +.. code-block:: python + + load("@aspect_rules_py//py:defs.bzl", "py_binary") + load("@score_tooling//bazel/rules/rules_score:sphinx_toolchain.bzl", "sphinx_toolchain") + + py_binary( + name = "score_build", + srcs = ["@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py"], + main = "@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py", + visibility = ["//visibility:public"], + deps = [ + "@score_tooling//bazel/rules/rules_score:sphinx_module_ext", + "@score_docs_as_code//src:plantuml_for_python", + "@score_docs_as_code//src/extensions/score_sphinx_bundle", + # Add your custom Sphinx extensions here + ], + ) + + sphinx_toolchain( + name = "score_sphinx_toolchain", + sphinx = ":score_build", + ) + + toolchain( + name = "my_toolchain", + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + toolchain = ":score_sphinx_toolchain", + toolchain_type = "@score_tooling//bazel/rules/rules_score:toolchain_type", + visibility = ["//visibility:public"], + ) + +**sphinx_toolchain parameters:** + +- ``sphinx`` — Label to the Sphinx build binary (mandatory) +- ``conf_template`` — Label to ``conf.py`` template (optional; default: ``@score_tooling//bazel/rules/rules_score:templates/conf.template.py``) +- ``html_merge_tool`` — Label to HTML merge tool (optional; default: ``@score_tooling//bazel/rules/rules_score:sphinx_html_merge``) + + +Cross-module dependencies +------------------------- + +``sphinx_module`` and ``dependable_element`` targets reference each other via +``deps`` to produce merged HTML output: + +.. code-block:: text + + /html/ + ├── index.html + ├── _static/ + ├── dependency1/ ← merged from first dep + └── dependency2/ ← merged from second dep + + +Complete Example +---------------- + +.. code-block:: python + + load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", + "architectural_design", "assumed_system_requirements", + "assumptions_of_use", "component", "component_requirements", + "dependability_analysis", "dependable_element", + "feature_requirements", "fmea", "unit") + + # Requirements + assumed_system_requirements(name = "sys_req", srcs = ["docs/sys_req.trlc"]) + feature_requirements(name = "features", srcs = ["docs/features.trlc"], + deps = [":sys_req"]) + component_requirements(name = "reqs", srcs = ["docs/reqs.trlc"], + deps = [":features"]) + assumptions_of_use(name = "aous", srcs = ["docs/aous.trlc"], + requirements = [":features"]) + + # Architecture + architectural_design(name = "arch", + static = ["docs/arch.puml"], + dynamic = ["docs/sequence.puml"], + public_api = ["docs/public_api.puml"]) + + # Safety analysis + fmea(name = "my_fmea", arch_design = ":arch", + controlmeasures = ["docs/controls.trlc"], + failuremodes = ["docs/failures.trlc"], + root_causes = ["docs/fta.puml"]) + dependability_analysis(name = "analysis", fmea = [":my_fmea"]) + + # Implementation + cc_library(name = "kvs_lib", srcs = ["kvs.cpp"], hdrs = ["kvs.h"]) + cc_test(name = "kvs_test", srcs = ["kvs_test.cpp"], deps = [":kvs_lib"]) + + # Structure + unit(name = "kvs_unit", unit_design = [":kvs_unit_design"], + implementation = [":kvs_lib"], tests = [":kvs_test"]) + component(name = "kvs_component", requirements = [":reqs"], + components = [":kvs_unit"], tests = []) + + # SEooC + dependable_element( + name = "persistency_kvs", + integrity_level = "B", + assumptions_of_use = [":aous"], + requirements = [":reqs"], + architectural_design = [":arch"], + dependability_analysis = [":analysis"], + components = [":kvs_component"], + tests = [], + deps = ["@score_process//:score_process_module"], + ) + +Build and test: + +.. code-block:: bash + + bazel build //:persistency_kvs + bazel test //:persistency_kvs + # HTML output: bazel-bin/persistency_kvs/html/ + + +Design Rationale +---------------- + +1. **Two-Tier Architecture** — Generic ``sphinx_module`` for flexibility; specialised artifact rules for safety-critical work products +2. **Dependency Management** — Automatic cross-referencing and HTML merging across modules +3. **Standardisation** — ``dependable_element`` enforces a consistent structure for all safety documentation +4. **Traceability** — Sphinx-needs integration enables bidirectional traceability +5. **Automation** — Index generation, symlinking, and ``conf.py`` management are automatic +6. **Build System Integration** — Bazel ensures reproducible, cacheable documentation builds + +Reference implementation: `examples/seooc `_ in the score-tooling repository. diff --git a/bazel/rules/rules_score/docs/overview.rst b/bazel/rules/rules_score/docs/overview.rst new file mode 100644 index 00000000..ff472d7c --- /dev/null +++ b/bazel/rules/rules_score/docs/overview.rst @@ -0,0 +1,49 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Overview +======== + +``rules_score`` organises safety-critical software artefacts into four groups: + +**Documentation Rule** — the generic Sphinx builder underlying all other rules: + +- :ref:`sphinx_module ` — Builds Sphinx HTML from RST/MD sources with dependency merging + +**Artifact Rules** — declare individual process work products: + +- :ref:`assumed_system_requirements ` — System-level requirements received from the wider context +- :ref:`feature_requirements ` — High-level feature specifications +- :ref:`component_requirements ` — Component-level requirements +- :ref:`assumptions_of_use ` — Safety-relevant operating conditions imposed on the integrator +- :ref:`architectural_design ` — Software architecture (static, dynamic, public API) +- :ref:`unit_design ` — Code-level design diagrams scoped to a single unit +- :ref:`fmea ` — Failure Mode and Effects Analysis (failure modes, FTA, control measures) +- :ref:`dependability_analysis ` — Complete safety analysis wrapping one or more FMEA targets + +**Structural Rules** — wire artefacts into a verifiable SEooC: + +- :ref:`unit ` — Smallest testable software element (design + implementation + tests) +- :ref:`component ` — Collection of units providing specific functionality +- :ref:`dependable_element ` — Complete SEooC with all artefacts assembled and validated + +All rules support cross-module dependencies for sphinx-needs integration and HTML merging. + +Architecture diagram +-------------------- + +.. uml:: _assets/rules_score_overview.puml + :align: center + :alt: Overview of rules_score architecture + :width: 100% diff --git a/bazel/rules/rules_score/docs/rule_reference.rst b/bazel/rules/rules_score/docs/rule_reference.rst new file mode 100644 index 00000000..b2e90a3d --- /dev/null +++ b/bazel/rules/rules_score/docs/rule_reference.rst @@ -0,0 +1,651 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Rule Reference +============== + +Documentation Rules +------------------- + +.. _rule-sphinx-module: + +sphinx_module +~~~~~~~~~~~~~ + +Builds Sphinx-based HTML documentation from RST/MD source files. Supports +cross-module dependencies and automatic HTML merging. + +.. code-block:: python + + sphinx_module( + name = "my_docs", + srcs = glob(["docs/**/*.rst", "docs/**/*.md"]), + index = "docs/index.rst", + deps = ["@external_module//:docs"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name; also the output directory prefix + * - ``srcs`` + - label list + - yes + - RST, MD, image, and PlantUML source files + * - ``index`` + - label + - yes + - Path to the root ``index.rst`` + * - ``deps`` + - label list + - no + - Other ``sphinx_module`` or ``dependable_element`` targets for cross-referencing and HTML merging (default ``[]``) + * - ``sphinx`` + - label + - no + - Override the Sphinx binary (default: toolchain-provided binary) + * - ``testonly`` + - bool + - no + - If ``True``, only testonly targets may depend on this (default ``False``) + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** ```` — Sphinx build; output under ``bazel-bin//html/`` + + +Artifact Rules +-------------- + +All artifact rules produce a ``SphinxSourcesInfo`` provider (documentation page) +and carry typed traceability information for downstream structural rules. + +.. _rule-assumed-system-req: + +assumed_system_requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +System-level requirements that the SEooC receives from its operational context. +These are too broad to be assigned to a single component. + +.. code-block:: python + + assumed_system_requirements( + name = "sys_req", + srcs = ["docs/assumed_system_requirements.trlc"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name + * - ``srcs`` + - label list + - yes + - ``.trlc`` files containing ``AssumedSystemReq`` records + * - ``deps`` + - label list + - no + - Reserved for consistency; unused at root level (default ``[]``) + * - ``visibility`` + - — + - no + - Bazel visibility (default ``["//visibility:public"]``) + +**Generated targets:** ```` (documentation), ``_test`` (TRLC syntax/type validation) + +.. _rule-feature-requirements: + +feature_requirements +~~~~~~~~~~~~~~~~~~~~ + +High-level requirements derived from ``AssumedSystemReq``. Operate at the +integration level; can only be satisfied by multiple components working together. + +.. code-block:: python + + feature_requirements( + name = "features", + srcs = ["docs/features.trlc"], + deps = [":sys_req"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name + * - ``srcs`` + - label list + - yes + - ``.trlc`` files containing ``FeatReq`` records + * - ``deps`` + - label list + - no + - ``assumed_system_requirements`` targets for ``derived_from`` cross-reference resolution (default ``[]``) + * - ``visibility`` + - — + - no + - Bazel visibility (default ``["//visibility:public"]``) + +**Generated targets:** ```` (documentation), ``_test`` (TRLC validation including cross-reference checks) + +.. _rule-component-requirements: + +component_requirements +~~~~~~~~~~~~~~~~~~~~~~ + +Requirements assigned to exactly one component; directly implementable and +testable within that component. + +.. code-block:: python + + component_requirements( + name = "comp_req", + srcs = ["docs/requirements.trlc"], + deps = [":features"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name + * - ``srcs`` + - label list + - yes + - ``.trlc`` files containing ``CompReq`` records + * - ``deps`` + - label list + - no + - ``feature_requirements`` or ``assumed_system_requirements`` targets for ``derived_from`` resolution (default ``[]``) + * - ``visibility`` + - — + - no + - Bazel visibility (default ``["//visibility:public"]``) + +**Generated targets:** ```` (documentation), ``_test`` (TRLC validation) + +.. _rule-assumptions-of-use: + +assumptions_of_use +~~~~~~~~~~~~~~~~~~ + +Conditions that the *integrating project* must fulfil when using this SEooC. + +.. code-block:: python + + assumptions_of_use( + name = "aous", + srcs = ["docs/assumptions.trlc"], + requirements = [":features"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name + * - ``srcs`` + - label list + - yes + - ``.trlc`` files containing ``AoU`` records + * - ``requirements`` + - label list + - no + - ``feature_requirements`` or ``component_requirements`` targets that these AoUs trace to (default ``[]``) + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** ```` (documentation), ``_test`` (TRLC validation) + +.. _rule-arch-design: + +.. _rule-architectural-design: + +architectural_design +~~~~~~~~~~~~~~~~~~~~ + +Bundles static, dynamic, and public-API architecture views into a single target. +Provides ``ArchitecturalDesignInfo`` consumed by ``dependable_element`` and ``fmea``. + +.. code-block:: python + + architectural_design( + name = "arch", + static = ["docs/static_design.puml"], + dynamic = ["docs/sequence.puml"], + public_api = ["docs/public_api.puml"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name + * - ``static`` + - label list + - no + - Static-view files (``.puml``, ``.rst``, ``.md``, ``.svg``, ``.png``) (default ``[]``) + * - ``dynamic`` + - label list + - no + - Dynamic-view files (default ``[]``) + * - ``public_api`` + - label list + - no + - Public-API diagram files (``.puml``); also generates traceability items for safety analysis (default ``[]``) + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** ```` (provides ``ArchitecturalDesignInfo``; no standalone test — consistency is validated as part of ``bazel test //pkg:my_element``) + +.. _rule-unit-design: + +unit_design +~~~~~~~~~~~ + +Attaches code-level design diagrams to a ``unit`` target. Accepts the same +file types as ``architectural_design`` but scoped to a single unit's internal +implementation. + +.. code-block:: python + + unit_design( + name = "my_unit_design", + static = ["class_diagram.rst", "class_diagram.puml"], + dynamic = ["sequence_diagram.puml"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name + * - ``static`` + - label list + - no + - Static-view files (class diagrams, data-structure diagrams). When an ``.rst`` fragment references a ``.puml`` via ``.. uml::``, list both files together (default ``[]``) + * - ``dynamic`` + - label list + - no + - Dynamic-view files (sequence, state diagrams) (default ``[]``) + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** ```` (no standalone test; diagrams are consumed by the parent ``unit``) + +.. _rule-fmea: + +fmea +~~~~ + +Bundles failure modes, control measures, and FTA diagrams into a single FMEA +documentation target. + +.. code-block:: python + + fmea( + name = "my_fmea", + failuremodes = ["docs/failuremodes.trlc"], + controlmeasures = ["docs/controlmeasures.trlc"], + root_causes = ["docs/fta.puml"], + arch_design = ":arch", + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name + * - ``failuremodes`` + - label list + - no + - ``.trlc`` files containing ``FailureMode`` records (default ``[]``) + * - ``controlmeasures`` + - label list + - no + - ``.trlc`` files containing ``ControlMeasure`` records (default ``[]``) + * - ``root_causes`` + - label list + - no + - FTA PlantUML diagram files (``.puml`` / ``.plantuml``) (default ``[]``) + * - ``arch_design`` + - label + - no + - ``architectural_design`` target for interface traceability (default ``None``) + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** ```` (documentation; no standalone test — traceability validated via the parent ``dependability_analysis``) + +.. _rule-dependability-analysis: + +dependability_analysis +~~~~~~~~~~~~~~~~~~~~~~ + +Wraps one or more ``fmea`` targets into a complete safety-analysis package. +Running ``bazel test`` validates the full FMEA traceability chain. + +.. code-block:: python + + dependability_analysis( + name = "analysis", + fmea = [":my_fmea"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 18 12 10 60 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name + * - ``fmea`` + - label list + - yes + - ``fmea`` targets to include in this analysis + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** ```` (build → documentation; ``bazel test //pkg:analysis`` → full FMEA traceability validation) + + +Structural Rules +---------------- + +.. _rule-unit: + +unit +~~~~ + +The smallest independently testable software element. Ties together +implementation targets, test targets, and an optional ``unit_design`` target. +The ``name`` must match the unit name used in the static architecture PlantUML +diagram. + +.. code-block:: python + + unit( + name = "KeyValueStore", + unit_design = [":kvs_unit_design"], + implementation = [":kvs_lib"], + tests = [":kvs_unit_test"], + ) + +.. list-table:: + :header-rows: 1 + :widths: 22 12 10 56 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name; **must match** the ``<>`` name in the static PlantUML diagram + * - ``unit_design`` + - label list + - yes + - ``unit_design`` targets describing the unit's internal design + * - ``implementation`` + - label list + - yes + - Library or binary targets that implement this unit (e.g. ``cc_library``, ``py_library``) + * - ``tests`` + - label list + - yes + - Test targets that verify this unit (may be ``[]``) + * - ``scope`` + - label list + - no + - Additional targets needed by the implementation but not listed in ``implementation`` (default ``[]``) + * - ``testonly`` + - bool + - no + - If ``True``, only testonly targets may depend on this unit (default ``True``) + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** ```` (provides ``UnitInfo``; no standalone test — the listed ``tests`` run via ``bazel test //pkg:...`` directly) + +.. _rule-component: + +component +~~~~~~~~~ + +Groups ``unit`` (and optionally nested ``component``) targets into a logical +subsystem. Associates component-level requirements and integration tests. +The ``name`` must match the component name in the static architecture PlantUML +diagram. + +.. code-block:: python + + component( + name = "KvsComponent", + requirements = [":comp_req"], + components = [":KeyValueStore", ":StorageBackend"], + tests = [], + ) + +.. list-table:: + :header-rows: 1 + :widths: 22 12 10 56 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name; **must match** the ``<>`` name in the static PlantUML diagram + * - ``requirements`` + - label list + - yes + - ``component_requirements`` or ``feature_requirements`` targets for this component + * - ``components`` + - label list + - no + - Nested ``unit`` or ``component`` targets (default ``[]``) + * - ``tests`` + - label list + - yes + - Integration test targets for the component as a whole (may be ``[]``) + * - ``testonly`` + - bool + - no + - If ``True``, only testonly targets may depend on this component (default ``True``) + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** ```` (provides ``ComponentInfo``; no standalone test — the listed ``tests`` run via ``bazel test //pkg:...`` directly) + +.. _rule-dependable-element: + +dependable_element +~~~~~~~~~~~~~~~~~~ + +Assembles all process artefacts into a complete SEooC. Runs Sphinx to generate +unified HTML documentation and enforces architecture consistency, traceability, +and scope checks at build/test time. + +.. code-block:: python + + dependable_element( + name = "my_seooc", + integrity_level = "B", + assumptions_of_use = [":aous"], + requirements = [":features"], + architectural_design = [":arch"], + dependability_analysis = [":analysis"], + components = [":kvs_component"], + tests = [], + ) + +.. list-table:: + :header-rows: 1 + :widths: 22 12 10 56 + + * - Attribute + - Type + - Required + - Description + * - ``name`` + - string + - yes + - Target name; also becomes the SEooC documentation title + * - ``integrity_level`` + - string + - yes + - ``"A"``, ``"B"``, ``"C"``, or ``"D"`` (D is highest: D > C > B > A) + * - ``assumptions_of_use`` + - label list + - yes + - ``assumptions_of_use`` targets + * - ``requirements`` + - label list + - yes + - ``feature_requirements`` or ``assumed_system_requirements`` targets + * - ``architectural_design`` + - label list + - yes + - ``architectural_design`` targets + * - ``dependability_analysis`` + - label list + - yes + - ``dependability_analysis`` targets + * - ``components`` + - label list + - yes + - ``component`` or ``unit`` targets that implement this SEooC + * - ``tests`` + - label list + - yes + - System-level test targets (may be ``[]``) + * - ``checklists`` + - label list + - no + - Additional ``.rst`` / ``.md`` checklist files (default ``[]``) + * - ``deps`` + - label list + - no + - Other ``dependable_element`` targets for cross-referencing and HTML merging (default ``[]``) + * - ``maturity`` + - string + - no + - ``"release"`` (default) or ``"development"`` — in development mode, scope violations and architecture errors are downgraded to warnings + * - ``testonly`` + - bool + - no + - If ``True``, only testonly targets may depend on this element (default ``True``) + * - ``visibility`` + - — + - no + - Bazel visibility + +**Generated targets:** + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Target + - Purpose + * - ```` + - Main target: build runs Sphinx; ``bazel test`` runs the traceability check + * - ``_doc`` + - Internal ``sphinx_module`` target; usable as ``deps`` in other Sphinx builds + * - ``_index`` + - Internal artefact-collection and architecture-validation target diff --git a/bazel/rules/rules_score/docs/safety_analysis.md b/bazel/rules/rules_score/docs/safety_analysis.md deleted file mode 100644 index 5685b947..00000000 --- a/bazel/rules/rules_score/docs/safety_analysis.md +++ /dev/null @@ -1,151 +0,0 @@ - - -# Safety Analysis - -This document shows the workflow and the interrelation on how to document an FMEA in TRLC / PlantUml / Lobster. - -## Overview - -The current proposal on how the Safety Analysis according ISO 26262 Pt9 shall be implemented is as described in the Model: - -![Safety Analysis](assets/safety_analysis.svg) - -## Implementation - -For the implementation of the Safety Analysis a Mix of TRLC and Plantuml is applied. The Verification itself is performed in lobster at the end. - -For the Definition and Verification of the Safetyanalysis itself two Bazel rules exist: - -```starlark -load("//bazel/rules/rules_score:rules_score.bzl", "dependability_analysis", "fmea") - -fmea( - name = "sample_fmea", - controlmeasures = [], - failuremodes = [], - root_causes = [], -) - -dependability_analysis( - name = "sample_dependability_analysis", - fmea = [":sample_fmea"], -) -``` - - -### Failuremode - -It starts with a Failuremode which was determined in an FMEA. This Failuremode is defined in a TRLC format: - -```trlc -package SampleLibrary - -import ScoreReq - -ScoreReq.FailureMode SampleFailureMode{ - guideword = ScoreReq.GuideWord.LossOfFunction - description = "SampleFailureMode takes over the world" - failureeffect = "The world as we know it will end" - version = 1 - safety = ScoreReq.Asil.B - interface = "SampleLibraryAPI.GetNumber" -} -``` - -### Root Causes -As described in the metamodel the root causes (aka BasicEvents) shall be identified by performing an FTA on each Failuremode. - -The FTA shall be modeled using Plantuml. Therefore a Metamodel was defined using plantuml procedures. It includes following entities: - -Events: -- $TopEvent($name, $alias) -- $IntermediateEvent($name, $alias, $connection) -- $BasicEvent($name, $alias, $connection) - -Gates: -- $AndGate($alias, $connection) -- $OrGate($alias, $connection) - -The Matching between TRLC and Plantuml shall be performed using the TRLC ID. This means that also the TopEvent of the FTA shall use the TRLC ID of the Failuremode. For our case the Failuremode was defined in the package SampleLibrary. Therefore the Full ID of the TRLC node is: -SampleLibrary.SampleFailureMode - -```plantuml -@startuml - -!include fta_metamodel.puml - -' Top level (skeleton) -$TopEvent("SampleFailureMode takes over the world", "SampleLibrary.SampleFailureMode") - -' 2nd level gates and events -$OrGate("OG1", "SampleLibrary.SampleFailureMode") - -$IntermediateEvent("SampleFailureMode is Angry", "IEF", "OG1") -$BasicEvent("Just bad luck", "SampleLibrary.JustBadLuck", "OG1") - -' 3rd level cascades from AGF -$AndGate("AG2", "IEF") -$BasicEvent("No More Cookies", "SampleLibrary.NoMoreCookies", "AG2") -$BasicEvent("No More Coffee", "SampleLibrary.NoMoreCoffee", "AG2") -@enduml - -``` - -![FTA Example](assets/fta_example.svg) - -## Control Measures -For each BasicEvent a Control Measure shall be derived. This will be performed again in TRLC. The mapping between the FTA BasicEvent and the ControlMeasure is established by **using the same TRLC ID**: the `ControlMeasure` record name (combined with its package) must match the alias of the `$BasicEvent` in the FTA diagram. - -For our case the BasicEvent is defined as: - -```plantuml -$BasicEvent("No More Cookies", "SampleLibrary.NoMoreCookies", "AG2") -``` - -The corresponding ControlMeasure must therefore be named `NoMoreCookies` in package `SampleLibrary`: - -```trlc -ScoreReq.ControlMeasure NoMoreCookies{ - safety = ScoreReq.Asil.B - description = "We shall only order family size cookie jars" - version = 1 -} -``` - -The traceability link is established automatically via matching of the fully-qualified name `SampleLibrary.NoMoreCookies`. - -## Traceability Report - -The `dependability_analysis` rule wrapping the `fmea` target is a Bazel test rule that runs a [lobster](https://github.com/bmw-software-engineering/lobster) traceability report via `lobster-ci-report`. - -To run the report and check the traceability chain (FTA events → Failure Modes / Control Measures): - -```bash -bazel test //bazel/rules/rules_score/examples/seooc:sample_dependability_analysis -``` - -## Tool Data Flow - -The diagram below shows how the input files are processed by each tool and assembled into the final lobster traceability report. - -![Tool Data Flow](assets/tool_data_flow.svg) - -| Input | Tool | Output | -|---|---|---| -| `public_api.puml` | `puml_parser --fbs-output-dir --lobster-output-dir` | `architecture.lobster` — Architecture interface items | -| `failuremodes.trlc` | `lobster-trlc` | `failuremodes.lobster` — Failure Mode requirements | -| `controlmeasures.trlc` | `lobster-trlc` | `controlmeasures.lobster` — Control Measure requirements | -| `fta.puml` | `safety_analysis_tools` | `root_causes.lobster` — FTA TopEvent / BasicEvent activities | -| all `.lobster` files | `lobster-ci-report` | `report.json` — traceability check result | -| `report.json` | `lobster-html-report` | `report.html` — human-readable HTML report | diff --git a/bazel/rules/rules_score/docs/user_guide/architectural_design.md b/bazel/rules/rules_score/docs/user_guide/architectural_design.md new file mode 100644 index 00000000..69960f62 --- /dev/null +++ b/bazel/rules/rules_score/docs/user_guide/architectural_design.md @@ -0,0 +1,244 @@ + + +# Architectural Design + +## Overview and Hierarchy + +Software in `rules_score` is structured in three levels: + +``` +dependable_element (SEooC — complete Safety Element out of Context) +└── component (groups units; owns component-level integration tests and requirements) + ├── unit (smallest independently verifiable architectural element: implementation + unit tests) + └── component (components can be nested for deeper hierarchies) + └── unit +``` + +Two rules apply: + +- `unit` targets must always be wrapped in a `component` — they cannot be placed directly under `dependable_element`. +- `component` targets can be nested: a component may contain other components as well as units, allowing arbitrary depth. + +This hierarchy exists for two complementary reasons: + +- **Interrelations between units and components** — A component defines a clear boundary within which its units collaborate. Grouping units into components keeps inter-unit coupling explicit and local, while the component's public interface controls what the rest of the system can depend on. The static architecture diagrams document exactly which components expose which interfaces, preventing accidental cross-boundary dependencies. +- **Interface-driven safety analysis** — The `public_api` diagrams at the SEooC level define the interfaces that external consumers may call. Failure modes in the FMEA reference individual interface items by name, establishing a direct traceability link from the safety analysis back to the architecture. Without a well-defined interface boundary this traceability would be impossible. + +Each level of the hierarchy has a corresponding design artifact: + +| Level | Design rule | Content | +|---|---|---| +| SEooC / component | `architectural_design` | Static structure, dynamic behaviour, public API | +| Unit | `unit_design` | Implementation Details, unit-level sequences | + +A consistency check at build time verifies that every component and unit in the Bazel implementation tree also appears in the PlantUML target architecture diagrams. If anything is out of sync the build fails with a descriptive error. + + +## Static Architecture + +The static view describes the **structural organisation** of your software: what components and units exist, how they relate to each other, and which dependencies they carry. It is the primary input for the architecture consistency check. + +### PlantUML + +Write a PlantUML class or component diagram that names every `component` and `unit` from your Bazel BUILD file. + +```{uml} ../_assets/MySeooc_StaticDesign.puml +:align: center +:alt: MySeooc static architecture +``` + +```text +@startuml MySeooc_StaticDesign + +package "MySeooc" as MySeooc <> { + component "KvsComponent" as KvsComponent <> { + component "KeyValueStore" as KeyValueStore <> + component "StorageBackend" as StorageBackend <> + } +} + +@enduml +``` + +### Bazel + +The PlantUML diagrams capture *intended* structure; the Bazel rules model the *actual* implementation. Using the same example as the diagram above — SEooC `MySeooc` containing component `KvsComponent` with units `KeyValueStore` and `StorageBackend` — the three rules work together like this: + +#### architectural_design + +declares which diagram files belong to which view category: + +```starlark +load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", "architectural_design") + +architectural_design( + name = "my_arch", + static = ["static_design.puml"], # the MySeooc_StaticDesign diagram above +) +``` + +#### unit + +one target per leaf unit (`<>` stereotype) in the diagram. The unit name must match the name used in the PlantUML. It ties together implementation targets, test targets, and an optional `unit_design` target (see {doc}`unit_design`): + +```starlark +load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", "unit") + +# Unit for KeyValueStore +cc_library(name = "kvs_lib", srcs = ["kvs.cpp"], hdrs = ["kvs.h"]) +cc_test (name = "kvs_unit_test", srcs = ["kvs_test.cpp"], deps = [":kvs_lib"]) + +unit( + name = "KeyValueStore", + unit_design = [":kvs_unit_design"], + implementation = [":kvs_lib"], + tests = [":kvs_unit_test"], +) + +# Unit for StorageBackend +cc_library(name = "storage_lib", srcs = ["storage_backend.cpp"], hdrs = ["storage_backend.h"]) +cc_test (name = "storage_unit_test", srcs = ["storage_test.cpp"], deps = [":storage_lib"]) + +unit( + name = "StorageBackend", + unit_design = [":storage_unit_design"], + implementation = [":storage_lib"], + tests = [":storage_unit_test"], +) +``` + + +#### component + +groups the units that belong to `KvsComponent` in the diagram. It aggregates one or more `unit` (or nested `component`) targets and links them to component-level requirements. Integration tests that verify the units working together are declared here: + +```starlark +load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", + "component", "component_requirements") + +component_requirements( + name = "kvs_comp_req", + srcs = ["component_requirements.trlc"], + deps = [":feature_req"], +) + +# The component maps to KvsComponent in the PlantUML diagram +component( + name = "KvsComponent", + requirements = [":kvs_comp_req"], + components = [":KeyValueStore", ":StorageBackend"], + tests = [], +) +``` + +## Dynamic Architecture + +The dynamic view describes **behavioural aspects** — sequences of interactions, state transitions, and activity flows. Dynamic diagrams document how your software behaves at runtime. They are not validated against the Bazel structure at build time. + +### PlantUML + +```{uml} ../_assets/MySeooc_WriteSequence.puml +:align: center +:alt: MySeooc write sequence +``` + +```text +@startuml MySeooc_WriteSequence + +actor Caller +participant KeyValueStore +participant StorageBackend + +Caller -> KeyValueStore : write(key, value) +KeyValueStore -> StorageBackend : flush() +StorageBackend --> KeyValueStore : OK +KeyValueStore --> Caller : Result::Ok + +@enduml +``` + +### Bazel + +```starlark +architectural_design( + name = "my_arch", + static = ["static_design.puml"], + dynamic = ["sequence.puml"], +) +``` + +## Public API + +The public API view describes the **interface your SEooC exposes to its environment**. These diagrams are linked to safety analysis: `FailureMode` records reference interface items by name (via the `interface` field), enabling traceability from each failure mode back to the architecture. + +### PlantUML + +```{uml} ../_assets/MySeooc_PublicApi.puml +:align: center +:alt: MySeooc public API +``` + +```text +@startuml MySeooc_PublicApi + +interface "KeyValueStore" as KVS { + + write(key: string, value: bytes): Result + + read(key: string): Optional +} + +@enduml +``` + +### Bazel + +```starlark +architectural_design( + name = "my_arch", + public_api = ["public_api.puml"], +) +``` + +The `public_api` attribute also generates traceability items that can be referenced by `fmea` targets (see {doc}`dependability_analysis`) via the `arch_design` attribute. + +(rst-and-markdown-wrappers)= +## RST and Markdown Wrappers + +When you want to combine a diagram with text, create an RST or Markdown file that embeds the diagram using the `.. uml::` directive (RST) or the MyST equivalent. + +**RST wrapper example:** + +```rst +Static Architecture +------------------- + +The following diagram shows the component structure of MySeooc. + +.. uml:: MySeooc_StaticDesign.puml +``` + +Include both the wrapper file *and* the referenced `.puml` file in the same Bazel list — the build needs both: + +```starlark +architectural_design( + name = "my_arch", + static = [ + "static_design.rst", # wrapper with prose + "MySeooc_StaticDesign.puml", # diagram referenced by the wrapper + ], +) +``` + +## Rule Reference: `architectural_design` + +For the complete `architectural_design` attribute reference, see {ref}`architectural_design ` in the rule index. diff --git a/bazel/rules/rules_score/docs/user_guide/dependability_analysis.md b/bazel/rules/rules_score/docs/user_guide/dependability_analysis.md new file mode 100644 index 00000000..e7a364c5 --- /dev/null +++ b/bazel/rules/rules_score/docs/user_guide/dependability_analysis.md @@ -0,0 +1,203 @@ + + +# Dependability Analysis + +The `dependability_analysis` rule is the top-level safety artifact in `rules_score`. It summarizes the dependability analyses (Safety / Security) which were performed for the dependable element. + +## Bazel Rule `dependability_analysis` + +```starlark +load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", + "dependability_analysis") + +dependability_analysis( + name = "my_da", + fmea = [":my_fmea"], +) +``` + +**Generated targets:** `` — build produces the documentation and traceability report; `bazel test` validates the full chain. + +## FMEA + +The Failure Mode and Effects Analysis (FMEA) is the core safety analysis method used by `dependability_analysis`. Each `fmea` target bundles four types of artifacts that must be linked together: + +| Artifact | Format | What it represents | +|---|---|---| +| **Public API Interfaces** | PlantUML (from `architectural_design.public_api`) | Interfaces where failures can manifest; referenced by `FailureMode.interface` | +| **Failure Modes** | TRLC (`.trlc`) | Effects identified in the FMEA: what can go wrong and its impact | +| **FTA Diagrams** | PlantUML (`.puml`) | Fault Tree Analysis: structural decomposition of each failure mode into root causes | +| **Control Measures** | TRLC (`.trlc`) | Countermeasures that address the root causes identified in the FTA | + +The public API connects the architectural view to the safety analysis: `FailureMode.interface` references an interface name defined in the `public_api` of the `architectural_design` target. + +The FTA artifacts are linked by a shared naming convention: the **TRLC fully-qualified record name** (package + record name) must match the **alias** used in the FTA PlantUML diagram. This is how traceability is established automatically in the report. + +### Failure Modes (TRLC) + +A failure mode is a `FailureMode` record in the `ScoreReq` model: + +```text +package MySeooc + +import ScoreReq + +ScoreReq.FailureMode FM_001 { + guideword = ScoreReq.GuideWord.LossOfFunction + description = "Key-value store returns stale data after power loss" + failureeffect = "Incorrect system state at startup" + safety = ScoreReq.Asil.B + interface = "KeyValueStore.read" + version = 1 +} +``` + +The TRLC fully-qualified name of this record is **`MySeooc.FM_001`**. This name is used as the `$TopEvent` alias in the FTA diagram. + +### FTA Diagrams (PlantUML) + +Each failure mode gets a Fault Tree Analysis diagram. A dedicated PlantUML metamodel (`fta_metamodel.puml`) provides the graphical elements — it is located at `plantuml/fta_metamodel.puml` in the score-tooling repository. Your diagram uses procedure calls from that metamodel; no standard PlantUML shapes are needed. + +Every `.puml` FTA file must begin with `!include fta_metamodel.puml` so that the procedure definitions are available. + +#### Available procedures + +| Procedure | Description | +|---|---| +| `$TopEvent(name, alias)` | The top-level failure mode. `alias` must equal the fully-qualified TRLC name of the corresponding `FailureMode` record (e.g. `MySeooc.FM_001`) | +| `$IntermediateEvent(name, alias, connection)` | An intermediate cause. `connection` is the **alias of the parent** node this event feeds into | +| `$BasicEvent(name, alias, connection)` | A root cause (leaf node). `alias` must equal the fully-qualified TRLC name of the corresponding `ControlMeasure` record. `connection` is the alias of the parent gate | +| `$AndGate(alias, connection)` | AND gate. All children must occur for the parent to trigger. `connection` is the alias of the parent node | +| `$OrGate(alias, connection)` | OR gate. Any single child is sufficient to trigger the parent. `connection` is the alias of the parent node | +| `$TransferInGate(name, alias, connection)` | Transfer-in gate linking to another FTA sub-tree | + +#### Linking procedures together + +Each element points to its **parent** via the `connection` parameter — the arrow goes *from* the element *up* to the parent. Build the tree bottom-up: + +1. Declare the `$TopEvent` first (no `connection` parameter — it is the root). +2. Declare gate(s) with `connection` set to the `$TopEvent` alias. +3. Declare `$BasicEvent` / `$IntermediateEvent` nodes with `connection` set to the enclosing gate's alias. + +``` +$TopEvent ← root, no connection + └── $OrGate(alias="OG_1", connection="TopEvent.alias") + ├── $BasicEvent(alias="CM_A", connection="OG_1") + └── $BasicEvent(alias="CM_B", connection="OG_1") +``` + +The `$BasicEvent` **alias IS the fully-qualified TRLC name** (`Package.RecordName`) of the corresponding `ControlMeasure` record. No separate linking step is needed — the naming convention is the link. + +#### Example FTA diagram + +```{uml} ../_assets/MySeooc_FTA.puml +:align: center +:alt: Example FTA diagram +``` + +```text +@startuml MySeooc_FTA +!include fta_metamodel.puml + +$TopEvent("KVS returns stale data after power loss", "MySeooc.FM_001") + +$OrGate("OG_1", "MySeooc.FM_001") + +$BasicEvent("Write not flushed to storage", "MySeooc.RC_001", "OG_1") +$BasicEvent("Corruption on unclean shutdown", "MySeooc.RC_002", "OG_1") + +@enduml +``` + +### Control Measures (TRLC) + +For each `$BasicEvent` in your FTA diagram, define a `ControlMeasure` record whose fully-qualified name matches the event alias: + +```text +package MySeooc + +import ScoreReq + +ScoreReq.ControlMeasure RC_001 { + description = "The KVS implementation shall use a write-ahead log and + flush it synchronously before acknowledging a write" + safety = ScoreReq.Asil.B + version = 1 +} + +ScoreReq.ControlMeasure RC_002 { + description = "On startup, the KVS shall detect and recover from + partially written records using the write-ahead log" + safety = ScoreReq.Asil.B + version = 1 +} +``` + +The alias `MySeooc.RC_001` in the FTA diagram matches the TRLC record `RC_001` in package `MySeooc`. This is how the traceability link is established. + +#### Other measure types + +The SCORE requirements model also defines `PreventiveMeasure` and `Mitigation`, both extending the same abstract `Measure` base type as `ControlMeasure`. Their Bazel and TRLC usage follows the same pattern; the record type name changes but the FTA alias convention (package + record name matching the `$BasicEvent` alias) is identical. + +### `fmea` — Bazel Rule + +For the complete `fmea` attribute reference, see {ref}`fmea ` in the rule index. + +## Traceability Validation + +Running `bazel test //my/package:my_da` executes a traceability check that validates the complete chain: + +``` + public_api interface ← FailureMode.interface + | + $TopEvent + | + AND / OR gate(s) + | + $BasicEvent + | + ControlMeasure +``` + +The check fails if: + +- A `$TopEvent` alias does not match any `FailureMode` record name +- A `$BasicEvent` alias does not match any `ControlMeasure` record name +- A `FailureMode` or `ControlMeasure` is defined but not referenced in any FTA diagram + +Fixing a traceability error means ensuring the naming convention is followed precisely: the fully-qualified TRLC name (package + record name, e.g. `MySeooc.RC_001`) must be used verbatim as the alias in the FTA diagram. + +## Example + +```{code-block} starlark +load( + "@score_tooling//bazel/rules/rules_score:rules_score.bzl", + "dependability_analysis", + "fmea", +) + +fmea( + name = "sample_fmea", + failuremodes = ["//bazel/rules/rules_score/examples/seooc/safety_analysis:sample_fmea_failure_modes.trlc"], + controlmeasures = ["//bazel/rules/rules_score/examples/seooc/safety_analysis:sample_fmea_control_measures.trlc"], + root_causes = ["//bazel/rules/rules_score/examples/seooc/safety_analysis:sample_fta.puml"], + arch_design = "//bazel/rules/rules_score/examples/seooc/design:sample_seooc_design", +) + +dependability_analysis( + name = "sample_dependability_analysis", + arch_design = "//bazel/rules/rules_score/examples/seooc/design:sample_seooc_design", + fmea = [":sample_fmea"], +) +``` diff --git a/bazel/rules/rules_score/docs/user_guide/general.md b/bazel/rules/rules_score/docs/user_guide/general.md new file mode 100644 index 00000000..21d9c177 --- /dev/null +++ b/bazel/rules/rules_score/docs/user_guide/general.md @@ -0,0 +1,113 @@ + + +# General Information + +`rules_score` provides a set of Bazel rules that help you build and document a **Safety Element out of Context (SEooC)** — a safety-critical software component developed independently and delivered with all the evidence needed for integration into a safety-relevant system. + +By declaring your workproducts (requirements, architecture, units, safety analysis) as Bazel targets, `rules_score` automatically verifies traceability and consistency of all workproducts and assembles them into a Sphinx HTML documentation including the traceability report. + +## The Dependable Element Concept + +A *dependable element* is the top-level unit of certification work. It bundles: + +| Artifact | What it contains | +|---|---| +| Assumed System Requirements | System-level requirements given as constraints from the surrounding context | +| Feature Requirements | Functional and safety requirements for this element | +| Assumptions of Use | Conditions the integrating project must satisfy | +| Architectural Design | Software Architectural Design in PlantUML | +| Software Units and Components | Implementation targets linked to their design | +| Dependability Analysis | FMEA, FTA diagrams and control measures | + +When you run `bazel build //:my_element`, all these pieces are assembled into a single HTML documentation site at `bazel-bin/my_element/html/`. + +## Build Flow + +The diagram below shows how your input files flow through the Bazel rules to produce the final outputs. + +```{uml} ../_assets/seooc_flow.puml +:align: center +:alt: SEooC build flow +:width: 90% +``` + +## Assembling a Dependable Element + +### Step 1 — Define your artifacts + +Define your requirements, architecture, units, and safety analysis using the rules described in the topic pages: + +- {doc}`requirements` — `assumed_system_requirements`, `feature_requirements`, `component_requirements`, `assumptions_of_use` +- {doc}`architectural_design` — `architectural_design`, `unit`, `component` +- {doc}`unit_design` — `unit_design` +- {doc}`dependability_analysis` — `fmea`, `dependability_analysis` + +### Step 2 — Wire them together + +```{code-block} starlark +dependable_element( + name = "safety_software_seooc_example", + architectural_design = ["//bazel/rules/rules_score/examples/seooc/design:sample_seooc_design"], + assumptions_of_use = [], + components = [":component_example"], + dependability_analysis = [":sample_dependability_analysis"], + integrity_level = "B", + requirements = ["//bazel/rules/rules_score/examples/seooc/docs/requirements:feature_requirements"], + tests = [], + deps = ["//bazel/rules/rules_score/examples/some_other_library:other_seooc"], +) +``` + +### Step 3 — Build + +```bash +bazel build //my/package:my_element +``` + +Output: + +``` +bazel-bin/my/package/my_element/html/ ← HTML documentation +bazel-bin/my/package/my_element_index/ ← traceability report (JSON + HTML) +``` + +Run traceability checks: + +```bash +bazel test //my/package:my_element +``` + +## Rule Reference `dependable_element` + +For the complete `dependable_element` attribute reference, see {ref}`dependable_element ` in the rule index. + +## Automatic Validations + +`rules_score` enforces the following constraints at **build time** — the build fails if any of them are violated: + +TODO: Link here the Test Specifications for the Validations for more details + +### Architecture consistency + +The components and units declared in `dependable_element.components` are compared against the static PlantUML diagrams in `architectural_design`. Every component or unit that appears in the implementation tree must also appear in the architecture diagrams. + +### Certified scope + +Every Bazel target that is transitively referenced through `unit.implementation` must fall within the package tree declared by the `unit` and `component` targets belonging to this element. External library dependencies that are not safety-certified must not appear there. + +When `maturity = "development"` is set, scope violations are printed as warnings instead of failing the build. Switch back to `"release"` before certification. + +### Integrity level + +A `dependable_element` with `integrity_level = "B"` must not depend (via `deps`) on another `dependable_element` with `integrity_level = "A"`. The hierarchy is D > C > B > A. diff --git a/bazel/rules/rules_score/docs/user_guide/index.rst b/bazel/rules/rules_score/docs/user_guide/index.rst new file mode 100644 index 00000000..20158d22 --- /dev/null +++ b/bazel/rules/rules_score/docs/user_guide/index.rst @@ -0,0 +1,25 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +User Guide +========== + +.. toctree:: + :maxdepth: 2 + + general + requirements + architectural_design + unit_design + dependability_analysis diff --git a/bazel/rules/rules_score/docs/user_guide/requirements.md b/bazel/rules/rules_score/docs/user_guide/requirements.md new file mode 100644 index 00000000..fbce74b4 --- /dev/null +++ b/bazel/rules/rules_score/docs/user_guide/requirements.md @@ -0,0 +1,220 @@ + + +# Requirements + +`rules_score` provides three rules for capturing different levels of requirements. + +## Requirement Hierarchy & Traceability + +``` +AssumedSystemReq → FeatReq → CompReq + (System) (Feature) (Component) + \ ↑ + \________________________/ +``` + +```{list-table} +:header-rows: 1 +:widths: 18 47 35 + +* - Type + - Description + - Traceability +* - **AssumedSystemReq** + - Requirements from the user / assumed system towards the SEooC. + + Too high-level for a single component — can only be satisfied by + multiple components working together. + - Root — no parent +* - **FeatReq** + - Refined requirements derived from `AssumedSystemReq`. + + Used when assumed system requirements are too high-level to be broken + down directly to one component — still require multiple components. + - **Must** reference ≥ 1 `AssumedSystemReq` via `derived_from` +* - **CompReq** + - Requirements assigned to exactly one component. + + Can be directly implemented and tested within that component. + - Optionally references ≥ 1 `FeatReq` via `derived_from` + using `[Package.FeatReq@version]` +``` + +Traceability is enforced by the trlc type system — version pinning (e.g. `@1`) ensures that when a parent requirement changes, all downstream references must be explicitly updated. + +Each rule consumes one or more `.trlc` source files and produces a target that carries both a Sphinx documentation page and traceability information for downstream rules. The TRLC Type Model (.rsl file) is already included in the rule. + +## Modeling Requirements in TRLC + +All requirements are written in [TRLC](https://github.com/bmw-software-engineering/trlc) (Traceability Requirements Language Checker). Each record maps to a specific `ScoreReq` type defined in the [S-CORE requirements model](https://github.com/eclipse-score/tooling/blob/main/bazel/rules/rules_score/trlc/config/score_requirements_model.rsl). + +For `TRLC` both a VSCode Extension and a LSP Server (e.g. for Clion) are [available](https://github.com/bmw-software-engineering/trlc-vscode-extension) + +### Assumed System Requirements + +System-level requirements that your SEooC receives from the wider context — for example, from a system specification. + +```text +package MySeooc + +import ScoreReq + +ScoreReq.AssumedSystemReq SYSREQ_001 { + description = "The system shall provide a real-time clock interface" + safety = ScoreReq.Asil.B + rationale = "Required for time-stamped log entries" + version = 1 +} +``` + +### Feature Requirements + +```text +package MySeooc + +import ScoreReq + +ScoreReq.FeatReq FEAT_001 { + description = "The component shall store key-value pairs persistently" + safety = ScoreReq.Asil.B + derived_from = [MySeooc.SYSREQ_001@1] + version = 1 +} +``` + +### Component Requirements + +`derived_from` uses the versioned tuple syntax `[Package.RecordId@version]`. + +```text +package MySeooc + +import ScoreReq + +ScoreReq.CompReq COMP_001 { + description = "Write operations shall complete within 5 ms" + safety = ScoreReq.Asil.B + derived_from = [MySeooc.FEAT_001@1] + version = 1 +} +``` + +### Assumptions of Use + +Conditions that the *integrating project* must satisfy when using your SEooC. The optional `mitigates` field describes (as a free-form string) the hazard or risk that is mitigated when this assumption is fulfilled. + +Traceability to requirements is established at the Bazel level via the `requirements` attribute on the `assumptions_of_use` rule — there is no TRLC `derived_from` or `satisfies` field on `AoU`. + +```text +package MySeooc + +import ScoreReq + +ScoreReq.AoU AOU_001 { + description = "The integrator shall ensure exclusive write access to the storage partition" + safety = ScoreReq.Asil.B + mitigates = "ConcurrentWriteCorruption" + version = 1 +} +``` + +## Allocation of Requirements to Architectural Elements + +Requirements are allocated to architectural elements differently depending on their level: + +**Component Requirements (`CompReq`)** +`CompReq` records are associated with exactly one component. The allocation is expressed implicitly through Bazel: the `component.requirements` attribute lists the `component_requirements` targets that belong to that component. Because a `component` maps directly to an architectural element in the static PlantUML diagram, the allocation to the architecture is established automatically. + +**Feature Requirements (`FeatReq`)** +`FeatReq` records operate at the integration level — they are too broad for a single component and can only be satisfied by multiple components working together. They are therefore allocated to the `dependable_element` as a whole via the Bazel `requirements` attribute: + +```starlark +dependable_element( + name = "my_element", + requirements = [":feature_requirements"], # FeatReq targets + ... +) +``` + +The traceability from `FeatReq` down to the components that implement it runs through the `component_requirements` chain (`FeatReq → CompReq → component`). + +## Modeling Requirements in Bazel Rules + +For the complete attribute reference for all requirements Bazel rules, see the rule index: + +- {ref}`assumed_system_requirements ` +- {ref}`feature_requirements ` +- {ref}`component_requirements ` +- {ref}`assumptions_of_use ` + +## Validation + +Every requirement target generates a `_test` target that runs `trlc --verify` on your `.trlc` sources. This check runs automatically as part of `bazel test ...`. + +The validation catches: + +- **Syntax errors** — malformed TRLC records +- **Type errors** — wrong value types for fields (e.g. a string where an enum is expected) +- **Mandatory field violations** — missing `description`, `safety`, or `version` +- **Broken cross-references** — a `derived_from` or `satisfies` pointing to a non-existent record +- **Unknown fields** — fields not defined in the S-CORE requirements model + +To run the validation for a single target: + +```bash +bazel test //my/package:my_feature_req_test +``` + +## AI-Powered Quality Check + +In addition to the structural TRLC validation described above, `rules_score` provides an optional AI-powered quality check for requirements via the `trlc_requirements_ai_test` rule. Unlike the structural check — which validates syntax, types, and cross-references — the AI check evaluates the *quality* of each requirement against requirements engineering guidelines (clarity, testability, completeness, etc.). + +### `trlc_requirements_ai_test` + +```starlark +load("@score_tooling//validation/ai_checker:ai_checker.bzl", + "trlc_requirements_ai_test") + +trlc_requirements_ai_test( + name = "feature_requirements_ai_check", + reqs = [":feature_requirements"], + score_threshold = "6.0", + tags = ["manual"], +) +``` + +The `tags = ["manual"]` attribute is strongly recommended to prevent the rule from running automatically during routine `bazel test //...` sweeps. The check requires a locally initialized copilot CLI or network access to an AI model in a cloudroom. + +Run the check explicitly with: + +```bash +bazel test //my/package:feature_requirements_ai_check --config=copilot +``` + +| Attribute | Type | Required | Description | +|---|---|---|---| +| `name` | string | yes | Target name | +| `reqs` | label list | yes | Requirement targets to analyse (any target providing `TrlcProviderInfo`, e.g. `feature_requirements`, `component_requirements`) | +| `model` | string | no | AI model identifier (default: `"anthropic/claude-sonnet-4-5"`) | +| `score_threshold` | string | no | Minimum average quality score from 0 to 10 to pass the test (default: `"0.0"`) | +| `guidelines` | label | no | Filegroup of guideline Markdown files to override the built-in guidelines | + +**Output files** (written to `bazel-bin/`): + +| File | Content | +|---|---| +| `_analysis.json` | Machine-readable scores, findings, and suggestions per requirement | +| `_analysis.html` | Interactive HTML report with colour-coded score cards and guideline references | + +**Prerequisites:** a GitHub Copilot licence (default) or a custom AI model configured via the `_custom_ai_model` attribute — see `https://github.com/eclipse-score/tooling/blob/main/validation/ai_checker/README.md` in the score-tooling repository for details. diff --git a/bazel/rules/rules_score/docs/user_guide/unit_design.md b/bazel/rules/rules_score/docs/user_guide/unit_design.md new file mode 100644 index 00000000..50a13217 --- /dev/null +++ b/bazel/rules/rules_score/docs/user_guide/unit_design.md @@ -0,0 +1,26 @@ + + +# Software Unit Design + +The `unit_design` rule documents the **internal implementation** of a single software unit — how its source code is structured, what data flows through it, and how it behaves at the code level. This is distinct from the higher-level architectural design diagrams (see {doc}`architectural_design`), which describe the intended component structure of the SEooC as a whole. + +A `unit_design` target is referenced by a `unit` target (see {doc}`architectural_design` — *Implementation Architecture in Bazel*) to attach code-level design artefacts to the unit. + +## `unit_design` — Code-Level Design Diagrams + +The `unit_design` rule attaches PlantUML diagrams to a unit. It uses the same `static` / `dynamic` category split as `architectural_design`, but scoped to a single unit's implementation. + +### `unit_design` Rule Reference + +For the complete `unit_design` attribute reference, see {ref}`unit_design ` in the rule index. diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.puml b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.puml index d5d43ef4..592cd28e 100644 --- a/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.puml +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.puml @@ -11,19 +11,6 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -' ******************************************************************************* -' Copyright (c) {year} Contributors to the Eclipse Foundation -' -' See the NOTICE file(s) distributed with this work for additional -' information regarding copyright ownership. -' -' This program and the accompanying materials are made available under the -' terms of the Apache License Version 2.0 which is available at -' https://www.apache.org/licenses/LICENSE-2.0 -' -' SPDX-License-Identifier: Apache-2.0 -' ******************************************************************************* - @startuml object "SEooC" as SEooC diff --git a/bazel/rules/rules_score/trlc/config/README.rst b/bazel/rules/rules_score/trlc/config/README.rst index c558e82f..328fd9b9 100644 --- a/bazel/rules/rules_score/trlc/config/README.rst +++ b/bazel/rules/rules_score/trlc/config/README.rst @@ -40,7 +40,6 @@ Type Hierarchy │ └── CompReq ├── derived_from (optional): FeatReqId[1..*] - ├── fulfilledBy (optional): String └── mitigates (optional): String Usage diff --git a/bazel/rules/rules_score/trlc/config/score_requirements_model.rsl b/bazel/rules/rules_score/trlc/config/score_requirements_model.rsl index ec07d2f3..c0a7bee2 100644 --- a/bazel/rules/rules_score/trlc/config/score_requirements_model.rsl +++ b/bazel/rules/rules_score/trlc/config/score_requirements_model.rsl @@ -27,91 +27,105 @@ enum Status { // Abstract Types /////////////////////////////// -abstract type Requirement { - description String - version Integer - note optional String - status Status - freeze status = Status.valid +abstract type Requirement "Base type for all S-CORE requirements." { + description "The normative requirement text. Must express an obligation (shall/should)." + String + version "Monotonically increasing version counter. Increment on every content change." + Integer + note "Non-normative explanatory text providing additional context." + optional String + status "Current lifecycle status of this requirement." + Status + freeze status = Status.valid } -abstract type RequirementSafety extends Requirement { - safety Asil +abstract type RequirementSafety "Extension of Requirement that carries an ASIL classification." extends Requirement { + safety "The Automotive Safety Integrity Level assigned to this requirement." + Asil } /////////////////////////////// // S-Core Requirements Model /////////////////////////////// -type AssumedSystemReq extends RequirementSafety { - rationale String +type AssumedSystemReq "System-level requirement received from the wider operational context (e.g. platform)." extends RequirementSafety { + rationale "Explanation of why this system-level requirement exists and how it was derived." + String } tuple AssumedSystemReqId { - item AssumedSystemReq - separator @ - version Integer + item AssumedSystemReq + separator @ + version Integer } -type FeatReq extends RequirementSafety { - derived_from AssumedSystemReqId[1 .. *] +type FeatReq "High-level feature requirement derived from one or more AssumedSystemReq items." extends RequirementSafety { + derived_from "One or more versioned references to the AssumedSystemReq items this feature requirement is derived from." + AssumedSystemReqId[1 .. *] } tuple FeatReqId { - item FeatReq - separator @ - version Integer + item FeatReq + separator @ + version Integer } -type CompReq extends RequirementSafety { - derived_from optional FeatReqId[1 .. *] - fulfilledBy optional String - mitigates optional String +type CompReq "Component-level requirement allocated to a specific software component." extends RequirementSafety { + derived_from "Versioned references to the FeatReq items this component requirement is derived from. Omit only for component-internal requirements with no feature-level parent." + optional FeatReqId[1 .. *] + mitigates "Reference to the FailureMode or safety concern that this requirement mitigates." + optional String } tuple CompReqId { - item CompReq - separator @ - version Integer + item CompReq + separator @ + version Integer } /////////////////////////////// // Safety Analyses /////////////////////////////// -enum GuideWord { - TooEarly - TooLate - Wrong - LossOfFunction - PartialFunction - UnintendedFunction - ExceedingFunction - DelayedFunction +enum GuideWord "HAZOP-style guide words used to categorise the nature of a failure mode." { + TooEarly "The function or signal occurs earlier than expected." + TooLate "The function or signal occurs later than expected." + Wrong "The function produces an incorrect value or signal." + LossOfFunction "The function is completely absent when it should be active." + PartialFunction "The function is active but only partially fulfils its purpose." + UnintendedFunction "The function is active when it should not be." + ExceedingFunction "The function operates beyond its specified bounds." + DelayedFunction "The function is active but with an unacceptable delay." } -type FailureMode extends ScoreReq.RequirementSafety { - guideword GuideWord - failureeffect String - rationale optional String - potentialcause optional String - interface optional String +type FailureMode "A potential failure of a system function, described using a HAZOP guide word." extends ScoreReq.RequirementSafety { + guideword "HAZOP guide word classifying the nature of this failure." + GuideWord + failureeffect "Description of the consequence of this failure mode on the system or its users." + String + rationale "Explanation of why this failure mode is considered relevant." + optional String + potentialcause "Description of the root cause or triggering condition for this failure mode." + optional String + interface "The system interface or signal that is affected by this failure mode." + optional String } -abstract type Measure extends ScoreReq.RequirementSafety { +abstract type Measure "Abstract base type for all safety measures. Cannot be instantiated directly." extends ScoreReq.RequirementSafety { } -type ControlMeasure extends Measure { +type ControlMeasure "A design or operational measure that detects, prevents, or limits a failure mode." extends Measure { } -type PreventiveMeasure extends Measure { +type PreventiveMeasure "A measure that prevents a failure mode from occurring in the first place." extends Measure { } -type Mitigation extends Measure { +type Mitigation "A measure that reduces the severity or probability of a failure mode's effect." extends Measure { } -type AoU extends ControlMeasure { - mitigates optional String +type AoU "Assumption of Use — a safety-relevant condition that a caller must fulfil when using this component." extends ControlMeasure { + mitigates "Reference to the FailureMode or hazard that is mitigated when this assumption is satisfied." + optional String } /////////////////////////////// diff --git a/cr_checker/resources/BUILD b/cr_checker/resources/BUILD index 05abe700..307734eb 100644 --- a/cr_checker/resources/BUILD +++ b/cr_checker/resources/BUILD @@ -26,3 +26,11 @@ filegroup( ], visibility = ["//visibility:public"], ) + +filegroup( + name = "exclusion", + srcs = [ + "exclusion.txt", + ], + visibility = ["//visibility:public"], +) diff --git a/cr_checker/resources/exclusion.txt b/cr_checker/resources/exclusion.txt new file mode 100644 index 00000000..73d6ffc0 --- /dev/null +++ b/cr_checker/resources/exclusion.txt @@ -0,0 +1 @@ +cr_checker/resources/templates.ini diff --git a/cr_checker/tool/cr_checker.py b/cr_checker/tool/cr_checker.py index 532d2c23..1cfe0506 100755 --- a/cr_checker/tool/cr_checker.py +++ b/cr_checker/tool/cr_checker.py @@ -408,35 +408,50 @@ def has_copyright(path, template, use_mmap, encoding, offset, config=None): return False -def has_duplicate_copyright(path, template, use_mmap, encoding, offset): +def has_any_copyright(path, use_mmap, encoding, offset): """ - Checks if the copyright header appears more than once in the file. + Checks if any copyright notice is present in the file header, regardless of format. Args: path (Path): A `pathlib.Path` object pointing to the file to check. - template (str): The copyright template to search for. use_mmap (bool): If True, uses memory-mapped file reading. encoding (str): Encoding type to use when reading the file. offset (int): Byte offset to skip (e.g. shebang line). Returns: - bool: True if the copyright header appears more than once, False otherwise. + bool: True if any copyright notice is found, False otherwise. """ load_text = load_text_from_file_with_mmap if use_mmap else load_text_from_file + content = load_text(path, BYTES_TO_READ, encoding, offset) + return bool( + re.search( + r"Copyright.*SPDX-License-Identifier", content, re.IGNORECASE | re.DOTALL + ) + ) - lines = template.splitlines(keepends=True) - regex_parts = [] - for line in lines: - stripped_line = line.rstrip("\n") - if BORDER_FILL_PATTERN.search(stripped_line): - regex_parts.append(line_to_flexible_regex(line)) - else: - formatted = line.format(year=r"\\d\{4\}\(-\\d\{4\}\)\?", author=r"\.\*") - regex_parts.append(convert_bre_to_regex(formatted)) - template_regex = "\n?".join(regex_parts) - content = load_text(path, 2 * BYTES_TO_READ, encoding, offset) - matches = list(re.finditer(template_regex, content)) +def has_duplicate_copyright(path, template, use_mmap, encoding, offset): + """ + Checks if more than one copyright notice is present in the file header. + + The check is format-agnostic: it counts occurrences of ``SPDX-License-Identifier`` + within a window of twice the template length, so that headers written by different + tools (e.g. REUSE vs. cr_checker) are both counted while string literals that + embed copyright text further into the file are ignored. + + Args: + path (Path): A `pathlib.Path` object pointing to the file to check. + template (str): The copyright template; its length defines the search window. + use_mmap (bool): If True, uses memory-mapped file reading. + encoding (str): Encoding type to use when reading the file. + offset (int): Byte offset to skip (e.g. shebang line). + + Returns: + bool: True if more than one copyright notice is found, False otherwise. + """ + load_text = load_text_from_file_with_mmap if use_mmap else load_text_from_file + content = load_text(path, 2 * len(template), encoding, offset) + matches = list(re.finditer(r"SPDX-License-Identifier", content, re.IGNORECASE)) if len(matches) > 1: LOGGER.debug("File %s has %d copyright headers.", path, len(matches)) return True @@ -500,6 +515,8 @@ def collect_inputs(inputs, exts=None): ): LOGGER.debug("Processing file: %s", item) all_files.append(item) + elif item.is_file(): + LOGGER.debug("Skipped (no configuration for file extension): %s", item) else: LOGGER.warning("Skipped (input is not a valid file or directory): %s", item) return all_files @@ -658,7 +675,11 @@ def process_files( elif not has_copyright( item, templates[key], use_mmap, encoding, effective_offset, config ): - if fix: + if has_any_copyright(item, use_mmap, encoding, effective_offset): + LOGGER.warning( + "Wrong copyright format in: %s, expected format from template", item + ) + elif fix: if remove_offset: remove_old_header(item, encoding, remove_offset) fix_result = fix_copyright( diff --git a/cr_checker/tool/pre-commit_wrapper b/cr_checker/tool/pre-commit_wrapper index 19a510ee..90f1f9de 100755 --- a/cr_checker/tool/pre-commit_wrapper +++ b/cr_checker/tool/pre-commit_wrapper @@ -31,5 +31,10 @@ if __name__ == "__main__": 4, os.path.normpath(os.path.join(script_dir, '..', 'resources', 'config.json')), ) + sys.argv.insert(5, '--exclusion-file') + sys.argv.insert( + 6, + os.path.normpath(os.path.join(script_dir, '..', 'resources', 'exclusion.txt')), + ) sys.exit(cr_checker.main(sys.argv[1:]))