From 2ca3c154ce65d1a94e17d74fe22b11f99acf86ec Mon Sep 17 00:00:00 2001 From: Jochen Hoenle <173445474+hoe-jo@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:00:26 +0200 Subject: [PATCH] fix filegroups in architectural design --- .../private/architectural_design.bzl | 45 ++++++++++++-- .../private/dependable_element.bzl | 3 +- bazel/rules/rules_score/test/BUILD | 62 +++++++++++++++++++ .../test_de_filegroup_arch_design.puml | 19 ++++++ bazel/rules/rules_score/test/seooc_test.bzl | 51 +++++++++++++++ 5 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 bazel/rules/rules_score/test/fixtures/test_de_filegroup_arch_design.puml diff --git a/bazel/rules/rules_score/private/architectural_design.bzl b/bazel/rules/rules_score/private/architectural_design.bzl index 1bcc02c4..26b71fa6 100644 --- a/bazel/rules/rules_score/private/architectural_design.bzl +++ b/bazel/rules/rules_score/private/architectural_design.bzl @@ -87,6 +87,34 @@ def _parse_puml_diagrams(ctx, files): lobster_outputs.append(lobster) return fbs_outputs, lobster_outputs +# Allowed file extensions per attribute. +# static/dynamic: diagram sources (.puml/.plantuml) and documentation (.rst/.md). +# public_api: diagram sources only — lobster traceability requires parseable diagrams. +_ALLOWED_EXTENSIONS = { + "static": ["puml", "plantuml", "rst", "md"], + "dynamic": ["puml", "plantuml", "rst", "md"], + "public_api": ["puml", "plantuml"], +} + +def _validate_file_extensions(ctx): + """Fail early if any resolved file has an unsupported extension. + + Works for both direct file labels and filegroup contents because + ctx.files.* flattens everything to individual File objects. + """ + for attr_name, allowed in _ALLOWED_EXTENSIONS.items(): + for f in getattr(ctx.files, attr_name): + if f.extension not in allowed: + fail( + "File '{}' passed to attribute '{}' of target '{}' has " + + "unsupported extension '.{}'. Allowed extensions: {}", + f.short_path, + attr_name, + ctx.label, + f.extension, + [".{}".format(e) for e in allowed], + ) + def _architectural_design_impl(ctx): """Implementation for architectural_design rule. @@ -104,6 +132,8 @@ def _architectural_design_impl(ctx): List of providers including DefaultInfo, ArchitecturalDesignInfo, SphinxSourcesInfo """ + _validate_file_extensions(ctx) + # Parse static and dynamic diagrams 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) @@ -172,17 +202,17 @@ _architectural_design = rule( "Automatically parses PlantUML files to produce FlatBuffers binary representations.", attrs = { "static": attr.label_list( - allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"], + allow_files = True, mandatory = False, - doc = "Static architecture diagrams (class diagrams, component diagrams, etc.)", + doc = "Static architecture diagrams. Accepts direct file labels, filegroups, or any target whose files include .puml/.plantuml (parsed to FlatBuffers) or .rst/.md (documentation).", ), "dynamic": attr.label_list( - allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"], + allow_files = True, mandatory = False, - doc = "Dynamic architecture diagrams (sequence diagrams, activity diagrams, etc.)", + doc = "Dynamic architecture diagrams. Accepts direct file labels, filegroups, or any target whose files include .puml/.plantuml (parsed to FlatBuffers) or .rst/.md (documentation).", ), "public_api": attr.label_list( - allow_files = [".puml", ".plantuml"], + allow_files = True, mandatory = False, doc = "Public API diagrams (parsed identically to static/dynamic). " + "Classified separately so their lobster items are exposed via " + @@ -213,7 +243,8 @@ def architectural_design( static = [], dynamic = [], public_api = [], - visibility = None): + visibility = None, + tags = []): """Define architectural design following S-CORE process guidelines. Architectural design documents describe the software architecture of a @@ -237,6 +268,7 @@ 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. + tags: List of Bazel tags to apply to the generated target. visibility: Bazel visibility specification for the generated targets. Generated Targets: @@ -265,4 +297,5 @@ def architectural_design( dynamic = dynamic, public_api = public_api, visibility = visibility, + tags = tags, ) diff --git a/bazel/rules/rules_score/private/dependable_element.bzl b/bazel/rules/rules_score/private/dependable_element.bzl index 3dbc8344..100a77d3 100644 --- a/bazel/rules/rules_score/private/dependable_element.bzl +++ b/bazel/rules/rules_score/private/dependable_element.bzl @@ -987,7 +987,8 @@ _dependable_element_index = rule( ), "architectural_design": attr.label_list( mandatory = True, - doc = "Architectural design targets or files.", + providers = [ArchitecturalDesignInfo], + doc = "Architectural design targets (architectural_design rule only).", ), "dependability_analysis": attr.label_list( mandatory = True, diff --git a/bazel/rules/rules_score/test/BUILD b/bazel/rules/rules_score/test/BUILD index bf9f99e0..7a51f089 100644 --- a/bazel/rules/rules_score/test/BUILD +++ b/bazel/rules/rules_score/test/BUILD @@ -68,8 +68,10 @@ load( ) load( ":seooc_test.bzl", + "arch_design_invalid_extension_test", "seooc_artifacts_copied_test", "seooc_description_test", + "seooc_filegroup_arch_design_test", "seooc_index_generation_test", "seooc_needs_provider_test", "seooc_sphinx_module_generated_test", @@ -183,6 +185,31 @@ architectural_design( static = ["fixtures/test_dependable_element_nested.puml"], ) +# Filegroup fixture: a plain RST file wrapped in a filegroup, used to test +# that architectural_design.static accepts filegroup targets (not just direct file labels). +filegroup( + name = "static_arch_filegroup", + srcs = ["fixtures/seooc_test/static_architecture.rst"], +) + +# architectural_design that mixes a filegroup (RST docs) and a direct .puml file. +# The .puml provides the SEooC declaration with no components (matching components=[]); +# the filegroup exercises filegroup support. +architectural_design( + name = "arch_design_with_filegroup", + static = [ + ":static_arch_filegroup", + "fixtures/test_de_filegroup_arch_design.puml", + ], +) + +# Filegroup with an unsupported file extension (.cc) — used by the failure test +# that verifies architectural_design rejects bad file types at analysis time. +filegroup( + name = "invalid_ext_filegroup", + srcs = ["fixtures/mock_lib1.cc"], +) + # - Safety Analysis (DFA): wp__sw_component_dfa # - Safety Analysis (FMEA): wp__sw_component_fmea dependability_analysis( @@ -414,6 +441,21 @@ dependable_element( tests = [], ) +# Dependable element that passes a filegroup into architectural_design via a wrapper +# architectural_design target. Exercises the filegroup-in-static path. +dependable_element( + name = "test_de_filegroup_arch_design", + testonly = True, + architectural_design = [":arch_design_with_filegroup"], + assumptions_of_use = [":aous"], + components = [], + dependability_analysis = [":dependability_analysis_target"], + description = "Test dependable element using a filegroup inside architectural_design.", + integrity_level = "B", + requirements = [":feat_req"], + tests = [], +) + # ============================================================================ # Test Instantiations - HTML Generation Tests # ============================================================================ @@ -506,6 +548,24 @@ seooc_description_test( target_under_test = ":seooc_test_lib_index", ) +seooc_filegroup_arch_design_test( + name = "seooc_filegroup_arch_design_test", + target_under_test = ":test_de_filegroup_arch_design_index", +) + +# Failure test: architectural_design must reject files with unsupported extensions +# even when they arrive via a filegroup. +architectural_design( + name = "arch_design_invalid_ext", + static = [":invalid_ext_filegroup"], + tags = ["manual"], +) + +arch_design_invalid_extension_test( + name = "arch_design_invalid_extension_test", + target_under_test = ":arch_design_invalid_ext", +) + # ============================================================================ # Test Suites # ============================================================================ @@ -570,6 +630,8 @@ sphinx_module_providers_test_suite(name = "sphinx_module_providers_tests") test_suite( name = "seooc_tests", tests = [ + ":arch_design_invalid_extension_test", + ":seooc_filegroup_arch_design_test", ":seooc_tests_artifacts_copied", ":seooc_tests_description", ":seooc_tests_index_generation", diff --git a/bazel/rules/rules_score/test/fixtures/test_de_filegroup_arch_design.puml b/bazel/rules/rules_score/test/fixtures/test_de_filegroup_arch_design.puml new file mode 100644 index 00000000..65018d6f --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/test_de_filegroup_arch_design.puml @@ -0,0 +1,19 @@ +' ******************************************************************************* +' 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 test_de_filegroup_arch_design + +package "Test DE Filegroup Arch Design" as test_de_filegroup_arch_design <> { +} + +@enduml diff --git a/bazel/rules/rules_score/test/seooc_test.bzl b/bazel/rules/rules_score/test/seooc_test.bzl index f5a16835..4e44af1f 100644 --- a/bazel/rules/rules_score/test/seooc_test.bzl +++ b/bazel/rules/rules_score/test/seooc_test.bzl @@ -145,3 +145,54 @@ def _seooc_description_test_impl(ctx): seooc_description_test = analysistest.make( impl = _seooc_description_test_impl, ) + +def _seooc_filegroup_arch_design_test_impl(ctx): + """Test that dependable_element works when a filegroup is used inside + architectural_design.static. + + Verifies: + - index.rst is generated + - The RST file contributed by the filegroup is present in the output files + under the architectural_design/ directory. + """ + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + files = target_under_test[DefaultInfo].files.to_list() + basenames = [f.basename for f in files] + + asserts.true( + env, + "index.rst" in basenames, + "Expected index.rst to be generated when architectural_design contains a filegroup", + ) + + # The filegroup contributes static_architecture.rst; it must appear in outputs + # symlinked under the architectural_design/ subtree. + arch_rst_files = [f for f in files if f.basename == "static_architecture.rst"] + asserts.true( + env, + len(arch_rst_files) > 0, + "Expected static_architecture.rst (from filegroup) to be in output files under architectural_design/", + ) + + return analysistest.end(env) + +seooc_filegroup_arch_design_test = analysistest.make( + impl = _seooc_filegroup_arch_design_test_impl, +) + +def _arch_design_invalid_extension_test_impl(ctx): + """Test that architectural_design fails with a clear error when a filegroup + contains a file with an unsupported extension (e.g. .cc).""" + env = analysistest.begin(ctx) + asserts.expect_failure( + env, + "unsupported extension", + ) + return analysistest.end(env) + +arch_design_invalid_extension_test = analysistest.make( + impl = _arch_design_invalid_extension_test_impl, + expect_failure = True, +)