Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bazel/rules/rules_score/examples/seooc/design/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ architectural_design(
dynamic = [
"dynamic_design.puml",
],
internal_api = [
"internal_api.puml",
],
public_api = [
"public_api.puml",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
participant "Unit 1" as unit_1 <<unit>>
participant "Unit 2" as unit_2 <<unit>>

unit_1 -> unit_2 : callMethod1()
unit_2 -> unit_1 : callMethod2()
unit_1 -> unit_2 : GetData()
unit_2 --> unit_1 : return : Data*

@enduml
22 changes: 22 additions & 0 deletions bazel/rules/rules_score/examples/seooc/design/internal_api.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
' *******************************************************************************
' 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
' *******************************************************************************

@startuml

package "safety_software_seooc_example" {
interface "InternalInterface" as InternalInterface <<interface>>{
{abstract} GetData(BindingType binding): Data*
}
}

@enduml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ package "Safety Software SEooC Example" as safety_software_seooc_example <<SEooC
component "Unit 1" as unit_1 <<unit>>
component "Unit 2" as unit_2 <<unit>>
}
interface "InternalInterface" as InternalInterface
unit_1 -( InternalInterface
unit_2 )- InternalInterface
}

package "SampleLibraryAPI" as SampleLibraryAPI
Expand Down
35 changes: 27 additions & 8 deletions bazel/rules/rules_score/private/architectural_design.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,10 @@ def _parse_puml_diagrams(ctx, files):
def _architectural_design_impl(ctx):
"""Implementation for architectural_design rule.

Collects architectural design artifacts including static and dynamic
diagrams, runs the PlantUML parser on .puml files to generate FlatBuffers
binaries, and provides them through the ArchitecturalDesignInfo provider.
Collects architectural design artifacts including static, dynamic, public
API, and internal API diagrams, runs the PlantUML parser on .puml files to
generate FlatBuffers binaries, and provides them through the
ArchitecturalDesignInfo provider.

The diagram type (component, class, sequence) is auto-detected by the
parser and encoded in the FlatBuffers binary via its schema root_type.
Expand All @@ -108,26 +109,28 @@ def _architectural_design_impl(ctx):
List of providers including DefaultInfo, ArchitecturalDesignInfo, SphinxSourcesInfo
"""

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

static_fbs = depset(static_fbs_list)
dynamic_fbs = depset(dynamic_fbs_list)
public_api_fbs = depset(public_api_fbs_list)
internal_api_fbs = depset(internal_api_fbs_list)
all_lobster = depset(static_lobster_list + dynamic_lobster_list + public_api_lobster_list)
public_api_lobster = depset(public_api_lobster_list)

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

# Run the linker on all generated .fbs.bin files to produce a
# plantuml_links.json for the clickable_plantuml Sphinx extension.
all_fbs_files = static_fbs.to_list() + dynamic_fbs.to_list() + public_api_fbs.to_list()
all_fbs_files = static_fbs.to_list() + dynamic_fbs.to_list() + public_api_fbs.to_list() + internal_api_fbs.to_list()
plantuml_links_json = ctx.actions.declare_file(
"{}/plantuml_links.json".format(ctx.label.name),
)
Expand All @@ -154,7 +157,7 @@ def _architectural_design_impl(ctx):
# toctree entry in the dependable_element index.
rst_wrappers = make_puml_rst_wrappers(
ctx,
ctx.files.static + ctx.files.dynamic + ctx.files.public_api,
ctx.files.static + ctx.files.dynamic + ctx.files.public_api + ctx.files.internal_api,
ctx.label.name,
ctx.file._puml_rst_template,
)
Expand All @@ -166,6 +169,7 @@ def _architectural_design_impl(ctx):
ArchitecturalDesignInfo(
static = static_fbs,
dynamic = dynamic_fbs,
internal_api = internal_api_fbs,
name = ctx.label.name,
lobster_files = all_lobster,
public_api_lobster_files = public_api_lobster,
Expand Down Expand Up @@ -206,6 +210,13 @@ _architectural_design = rule(
"public_api_lobster_files, enabling failure-mode-to-interface " +
"traceability at the dependable element level.",
),
"internal_api": attr.label_list(
allow_files = [".puml", ".plantuml"],
mandatory = False,
doc = "Internal API diagrams (class diagrams). " +
"Classified separately so their FlatBuffers outputs are exposed via " +
"ArchitecturalDesignInfo.internal_api for downstream validation.",
),
"_puml_parser": attr.label(
default = Label("@score_tooling//plantuml/parser:parser"),
executable = True,
Expand Down Expand Up @@ -237,6 +248,7 @@ def architectural_design(
static = [],
dynamic = [],
public_api = [],
internal_api = [],
**kwargs):
"""Define architectural design following S-CORE process guidelines.

Expand All @@ -261,6 +273,11 @@ def architectural_design(
diagrams but classified separately so their lobster items are
exposed via public_api_lobster_files, enabling failure-mode-to-
interface traceability at the dependable element level.
internal_api: Optional list of .puml files describing internal
interfaces of this element. These are parsed identically to
static/dynamic diagrams but classified separately so their
FlatBuffers outputs are exposed via ArchitecturalDesignInfo.
internal_api for downstream validation.
visibility: Bazel visibility specification for the generated targets.

Generated Targets:
Expand All @@ -279,6 +296,7 @@ def architectural_design(
"sequence_diagram.puml",
"activity_diagram.puml",
],
internal_api = ["internal_api.puml"],
)
```
"""
Expand All @@ -288,5 +306,6 @@ def architectural_design(
static = static,
dynamic = dynamic,
public_api = public_api,
internal_api = internal_api,
**kwargs
)
18 changes: 15 additions & 3 deletions bazel/rules/rules_score/private/dependable_element.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -652,14 +652,15 @@ def _collect_architecture_components(ctx):

return all_components

def _run_validation(ctx, arch_json, static_fbs_files, dynamic_fbs_files, unit_static_fbs_files):
def _run_validation(ctx, arch_json, static_fbs_files, dynamic_fbs_files, internal_api_fbs_files, unit_static_fbs_files):
"""Run the architecture verifier tool against a pre-built JSON file.

Args:
ctx: Rule context
arch_json: The architecture JSON File object (already declared and written)
static_fbs_files: List of static component-diagram FlatBuffer files
dynamic_fbs_files: List of dynamic component-diagram FlatBuffer files
internal_api_fbs_files: List of internal-API FlatBuffer files
unit_static_fbs_files: List of static class-diagram FlatBuffer files

Returns:
Expand All @@ -674,6 +675,8 @@ def _run_validation(ctx, arch_json, static_fbs_files, dynamic_fbs_files, unit_st
validation_args.add_all("--component-fbs", static_fbs_files)
if dynamic_fbs_files:
validation_args.add_all("--sequence-fbs", dynamic_fbs_files)
if internal_api_fbs_files:
validation_args.add_all("--internal-api-fbs", internal_api_fbs_files)

# if unit_static_fbs_files:
# validation_args.add_all("--class-fbs", unit_static_fbs_files)
Expand All @@ -684,7 +687,7 @@ def _run_validation(ctx, arch_json, static_fbs_files, dynamic_fbs_files, unit_st

# ctx.actions.run will fail the build if validation_cli returns non-zero exit code
ctx.actions.run(
inputs = [arch_json] + static_fbs_files + dynamic_fbs_files + unit_static_fbs_files,
inputs = [arch_json] + static_fbs_files + dynamic_fbs_files + internal_api_fbs_files + unit_static_fbs_files,
outputs = [validation_log],
executable = ctx.executable._validation_cli,
arguments = [validation_args],
Expand Down Expand Up @@ -864,10 +867,12 @@ def _dependable_element_index_impl(ctx):
# static architecture) and verify them against the current architecture.
static_fbs_files = []
dynamic_fbs_files = []
internal_api_fbs_files = []
for ad in ctx.attr.architectural_design:
if ArchitecturalDesignInfo in ad:
static_fbs_files.extend(ad[ArchitecturalDesignInfo].static.to_list())
dynamic_fbs_files.extend(ad[ArchitecturalDesignInfo].dynamic.to_list())
internal_api_fbs_files.extend(ad[ArchitecturalDesignInfo].internal_api.to_list())

# Collect class-diagram FBS files produced by unit_design targets.
unit_static_fbs_files = []
Expand All @@ -876,7 +881,14 @@ def _dependable_element_index_impl(ctx):
unit_static_fbs_files.extend(unit_info.unit_design_static_fbs.to_list())

# Run validation; build fails automatically on non-zero exit
validation_log = _run_validation(ctx, arch_json, static_fbs_files, dynamic_fbs_files, unit_static_fbs_files)
validation_log = _run_validation(
ctx,
arch_json,
static_fbs_files,
dynamic_fbs_files,
internal_api_fbs_files,
unit_static_fbs_files,
)

# Both outputs are included so validation always runs in a default build.
# validation_log is also exposed in the debug output group for explicit access.
Expand Down
1 change: 1 addition & 0 deletions bazel/rules/rules_score/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ ArchitecturalDesignInfo = provider(
fields = {
"static": "Depset of FlatBuffers binaries for static architecture diagrams (class diagrams, component diagrams, etc.)",
"dynamic": "Depset of FlatBuffers binaries for dynamic architecture diagrams (sequence diagrams, activity diagrams, etc.)",
"internal_api": "Depset of FlatBuffers binaries for internal API diagrams (class diagrams, etc.)",
"name": "Name of the architectural design target",
"lobster_files": "Depset of .lobster traceability files generated by the PlantUML parser from component diagrams.",
"public_api_lobster_files": "Depset of .lobster traceability files generated from public_api diagrams (subset of lobster_files).",
Expand Down
1 change: 1 addition & 0 deletions validation/core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ rust_library(
"src/validators/component_class_validator.rs",
"src/validators/component_sequence_validator.rs",
"src/validators/mod.rs",
"src/validators/test/component_sequence_validator_test.rs",
],
crate_root = "src/lib.rs",
visibility = ["//visibility:public"],
Expand Down
32 changes: 21 additions & 11 deletions validation/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,18 @@ The current implementation supports three validation flows:
2. `ComponentClass`: compares component-diagram unit IDs with enclosing
namespace IDs observed in class diagrams using boundary-aware suffix
matching.
3. `ComponentSequence`: compares component-diagram unit aliases with
caller/callee participants observed in sequence diagrams (exact match).
3. `ComponentSequence`: checks that component-diagram unit aliases, shared
interface relations, and sequence-diagram function-call connections stay in
sync. When internal API diagrams are provided, it also checks that each
sequence function name is declared on a shared interface referenced by both
participating units.

The CLI builds a `ValidationContext` from the provided inputs, infers which of
these flows are executable, and runs all compatible validators in one pass.
Internal API diagrams are handled separately from regular class diagrams.
If no `--internal-api-fbs` inputs are provided, `ComponentSequence` still runs
the alias and interface-connection checks and skips method-level validation.

The CLI inspects the provided inputs, determines which validations can run,
and executes all compatible checks in one pass.

## Layering

Expand All @@ -59,20 +66,22 @@ model construction.

The CLI accepts the following input families:

- `--architecture-json`: Bazel architecture export consumed by `BazelReader`
- `--component-fbs`: one or more component-diagram FlatBuffers files consumed by
`ComponentDiagramReader`
- `--sequence-fbs`: one or more sequence-diagram FlatBuffers files consumed by
`SequenceDiagramReader`
- `--class-fbs`: one or more class-diagram FlatBuffers files consumed by
`ClassDiagramReader`
- `--architecture-json`: Bazel architecture export
- `--component-fbs`: one or more component-diagram FlatBuffers files
- `--sequence-fbs`: one or more sequence-diagram FlatBuffers files
- `--class-fbs`: one or more class-diagram FlatBuffers files
- `--internal-api-fbs`: optional internal-API FlatBuffers files for the
`ComponentSequence` validator

The current inference rules are:

- `--architecture-json` + `--component-fbs` enables `BazelComponent`
- `--component-fbs` + `--class-fbs` enables `ComponentClass`
- `--component-fbs` + `--sequence-fbs` enables `ComponentSequence`

`--internal-api-fbs` is an optional additional input for
`ComponentSequence`. It does not enable a validator on its own.

If multiple combinations are present, all compatible validators are executed.

## Run
Expand All @@ -91,6 +100,7 @@ bazel run //validation/core:validation_cli -- \
--component-fbs path/to/component.fbs.bin \
--sequence-fbs path/to/sequence.fbs.bin \
--class-fbs path/to/class.fbs.bin \
--internal-api-fbs path/to/internal_api.fbs.bin \
--output path/to/validation.log
```

Expand Down
8 changes: 5 additions & 3 deletions validation/core/docs/specifications/component_sequence.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ participant "Unit 2" as unit_2
Every pair of units connected through an interface in the component diagram
must have at least one corresponding function-call interaction in the sequence
diagrams, and every cross-unit function call in a sequence diagram must
correspond to an interface connection in the component diagram.
correspond to an interface connection in the component diagram. The caller
shall be the consumer of the shared interface and the callee shall be the
provider. Self-calls are excluded from this check.
*(Requirement: {requirement:downstream-ref}`Tools.ComponentSequenceInterfaceConnectionConsistency`)*

```text
Expand Down Expand Up @@ -121,6 +123,7 @@ unit_1 -> unit_2 : SetData(d)
| Unexpected sequence participant | Alias Consistency |
| Missing sequence interaction for interface-connected units | Interface-Connection Consistency |
| Missing interface connection for sequence-connected units | Interface-Connection Consistency |
| Invalid consumer/provider roles | Interface-Connection Consistency |
| Missing internal API interface | Method-Name Consistency |
| Method not declared in related interface | Method-Name Consistency |
| Interface function not exercised | Interface Coverage |
Expand All @@ -130,9 +133,8 @@ unit_1 -> unit_2 : SetData(d)
The validator emits debug output containing:

- expected unit aliases
- observed caller/callee participants
- observed participants
- observed sequence calls (`caller -> callee : method`)
- observed function-call connections (`caller <-> callee`)
- unit interface targets derived from the component diagram
- interface-connected unit pairs derived from the component diagram
- internal API interfaces found and checked for method validation, when
Expand Down
9 changes: 9 additions & 0 deletions validation/core/integration_test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,19 @@ filegroup(
filegroup(
name = "component_sequence_test_data",
srcs = [
"//validation/core/integration_test/component_sequence/negative_interface_function_not_exercised:case_data",
"//validation/core/integration_test/component_sequence/negative_invalid_consumer_provider_direction:case_data",
"//validation/core/integration_test/component_sequence/negative_method_missing_from_internal_api:case_data",
"//validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units:case_data",
"//validation/core/integration_test/component_sequence/negative_missing_method_in_related_interface:case_data",
"//validation/core/integration_test/component_sequence/negative_missing_participant:case_data",
"//validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units:case_data",
"//validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation:case_data",
"//validation/core/integration_test/component_sequence/negative_mixed_mismatch:case_data",
"//validation/core/integration_test/component_sequence/negative_orphan_participant:case_data",
"//validation/core/integration_test/component_sequence/positive_exact_match:case_data",
"//validation/core/integration_test/component_sequence/positive_internal_api_method_match:case_data",
"//validation/core/integration_test/component_sequence/positive_self_call_method_match:case_data",
],
)

Expand Down
Loading
Loading