diff --git a/bazel/rules/rules_score/private/assumed_system_requirements.bzl b/bazel/rules/rules_score/private/assumed_system_requirements.bzl index 90ff590c..ec044f9c 100644 --- a/bazel/rules/rules_score/private/assumed_system_requirements.bzl +++ b/bazel/rules/rules_score/private/assumed_system_requirements.bzl @@ -51,8 +51,9 @@ def assumed_system_requirements( deps: Optional list of requirement targets whose TRLC records are needed for cross-reference parsing. These targets must provide TrlcProviderInfo. Typically empty for top-level system requirements. - spec: Optional TRLC specification target providing RSL type definitions. - Defaults to the S-CORE requirements model + spec: TRLC specification target(s) providing RSL type definitions. + Accepts a single label or a list of labels; all are merged into the + spec passed to TRLC. Defaults to the S-CORE requirements model (``@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model``). Override this when using a custom requirements model. lobster_config: Optional Lobster extraction config label. Defaults to the diff --git a/bazel/rules/rules_score/private/component_requirements.bzl b/bazel/rules/rules_score/private/component_requirements.bzl index 1ad91911..79a86b63 100644 --- a/bazel/rules/rules_score/private/component_requirements.bzl +++ b/bazel/rules/rules_score/private/component_requirements.bzl @@ -50,10 +50,10 @@ def component_requirements( deps: Optional list of requirement targets (e.g. assumed_system_requirements, feature_requirements) whose TRLC records are needed for cross-reference parsing. These targets must provide TrlcProviderInfo. - spec: Optional TRLC specification target providing RSL type definitions. - Defaults to the S-CORE requirements model + spec: TRLC specification target(s) providing RSL type definitions. + Accepts a single label or a list of labels; all are merged into the + spec passed to TRLC. Defaults to the S-CORE requirements model (``@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model``). - Override this when using a custom requirements model. lobster_config: Optional Lobster extraction config label. Defaults to the S-CORE component requirement config. visibility: Bazel visibility specification for the generated targets. diff --git a/bazel/rules/rules_score/private/feature_requirements.bzl b/bazel/rules/rules_score/private/feature_requirements.bzl index f376fa0a..15adafbf 100644 --- a/bazel/rules/rules_score/private/feature_requirements.bzl +++ b/bazel/rules/rules_score/private/feature_requirements.bzl @@ -51,10 +51,10 @@ def feature_requirements( deps: Optional list of requirement targets (e.g. assumed_system_requirements) whose TRLC records are needed for cross-reference parsing. These targets must provide TrlcProviderInfo. - spec: Optional TRLC specification target providing RSL type definitions. - Defaults to the S-CORE requirements model + spec: TRLC specification target(s) providing RSL type definitions. + Accepts a single label or a list of labels; all are merged into the + spec passed to TRLC. Defaults to the S-CORE requirements model (``@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model``). - Override this when using a custom requirements model. lobster_config: Optional Lobster extraction config label. Defaults to the S-CORE feature requirement config. visibility: Bazel visibility specification for the generated targets. diff --git a/bazel/rules/rules_score/private/requirements.bzl b/bazel/rules/rules_score/private/requirements.bzl index 621706af..415f0c1d 100644 --- a/bazel/rules/rules_score/private/requirements.bzl +++ b/bazel/rules/rules_score/private/requirements.bzl @@ -24,6 +24,8 @@ load("@trlc//:trlc.bzl", "TrlcProviderInfo", "subrule_trlc_image_stage") load("//bazel/rules/rules_score:providers.bzl", "AssumedSystemRequirementsInfo", "AssumptionsOfUseInfo", "ComponentRequirementsInfo", "FeatureRequirementsInfo", "SphinxSourcesInfo") load("//bazel/rules/rules_score/private:rst_to_trlc.bzl", "rst_to_trlc") +_DEFAULT_SPEC = Label("//bazel/rules/rules_score/trlc/config:score_requirements_model") + # ============================================================================ # Private Rule Implementation # ============================================================================ @@ -50,7 +52,7 @@ def _requirements_impl(ctx): transitive_reqs.append(trlc_info.reqs) transitive_reqs.append(trlc_info.deps) - own_spec_files = ctx.attr.spec[DefaultInfo].files + own_spec_files = depset(transitive = [t[DefaultInfo].files for t in ctx.attr.spec]) spec_depset = depset(transitive = [own_spec_files] + transitive_spec) deps_depset = depset(transitive = transitive_reqs) @@ -167,9 +169,9 @@ _score_requirements_rule = rule( mandatory = True, doc = "Lobster YAML configuration file for traceability extraction.", ), - "spec": attr.label( - default = Label("//bazel/rules/rules_score/trlc/config:score_requirements_model"), - doc = "TRLC specification target providing the RSL files that define the requirement types. Defaults to the S-CORE requirements model.", + "spec": attr.label_list( + default = [_DEFAULT_SPEC], + doc = "TRLC specification targets providing the RSL files that define the requirement types. The S-CORE requirements model is always included; this adds zero or more additional spec targets on top of it. Populated by the score_requirements_rule macro, which performs the merge.", ), "image_srcs": attr.label_list( allow_files = [".svg", ".png", ".puml"], @@ -192,7 +194,7 @@ def score_requirements_rule( req_kind, lobster_config, deps = [], - spec = Label("//bazel/rules/rules_score/trlc/config:score_requirements_model"), + spec = [], ref_package = "", **kwargs): """Macro wrapper around _score_requirements_rule with RST support. @@ -207,6 +209,9 @@ def score_requirements_rule( listed in the underlying rule's ``srcs``. Args: + spec: Additional TRLC specification target(s) (single label or list) + to merge in on top of the S-CORE requirements model, which is + always included automatically -- do not re-list it here. ref_package: TRLC package prefix used for derived_from cross-references when converting RST sources (e.g. "AssumedSystemRequirements" for feature requirements that derive from ASR). @@ -216,6 +221,8 @@ def score_requirements_rule( conversion), in the same order. Useful for callers that need to run trlc_requirements_test against the same source set. """ + extra_spec = spec if type(spec) == type([]) else [spec] + merged_spec = [_DEFAULT_SPEC] + [s for s in extra_spec if s != _DEFAULT_SPEC] trlc_srcs = [] extra_deps = [] resolved_srcs = [] @@ -242,7 +249,7 @@ def score_requirements_rule( deps = deps + extra_deps, req_kind = req_kind, lobster_config = lobster_config, - spec = spec, + spec = merged_spec, **kwargs ) diff --git a/bazel/rules/rules_score/test/BUILD b/bazel/rules/rules_score/test/BUILD index ff470fcd..7f45ded6 100644 --- a/bazel/rules/rules_score/test/BUILD +++ b/bazel/rules/rules_score/test/BUILD @@ -13,6 +13,8 @@ load("@pip_tooling_test//:requirements.bzl", "requirement") load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") +load("@rules_python//python:defs.bzl", "py_test") +load("@rules_shell//shell:sh_test.bzl", "sh_test") load( "@score_tooling//bazel/rules/rules_score:rules_score.bzl", "architectural_design", @@ -30,7 +32,7 @@ load( ) load("@score_tooling//bazel/rules/rules_score:sphinx_toolchain.bzl", "score_sphinx_toolchain") load("@score_tooling//cpp/libclang:libclang_toolchain.bzl", "libclang_toolchain") -load("@trlc//:trlc.bzl", "trlc_requirements", "trlc_requirements_test") +load("@trlc//:trlc.bzl", "trlc_requirements", "trlc_requirements_test", "trlc_specification") load( ":html_generation_test.bzl", "auto_config_generation_test", @@ -48,9 +50,11 @@ load( "lobster_config_test_suite", ) load( - ":requirements_image_test.bzl", - "image_srcs_sphinx_sources_test", - "requirements_image_test_suite", + ":requirements_multi_spec_test.bzl", + "asr_multi_spec_provider_test", + "comp_req_multi_spec_provider_test", + "feat_req_multi_spec_provider_test", + "requirements_multi_spec_test_suite", ) load( ":requirements_rst_test.bzl", @@ -1369,6 +1373,65 @@ aous_rst_sphinx_test( requirements_rst_test_suite(name = "requirements_rst_tests") +# ============================================================================ +# Multi-spec Requirements Tests +# ============================================================================ + +trlc_specification( + name = "extra_spec_fixture", + srcs = ["fixtures/multi_spec/extra_model.rsl"], +) + +assumed_system_requirements( + name = "asr_multi_spec", + srcs = ["fixtures/rst_requirements/assumed_system_requirements.rst"], + spec = [":extra_spec_fixture"], +) + +feature_requirements( + name = "feat_req_multi_spec", + srcs = ["fixtures/rst_requirements/feature_requirements.rst"], + ref_package = "AssumedSystemRequirements", + spec = [":extra_spec_fixture"], + deps = [":asr_multi_spec"], +) + +component_requirements( + name = "comp_req_multi_spec", + srcs = ["fixtures/rst_requirements/component_requirements.rst"], + ref_package = "FeatureRequirements", + spec = [":extra_spec_fixture"], + deps = [ + ":asr_multi_spec", + ":feat_req_multi_spec", + ], +) + +asr_multi_spec_provider_test( + name = "asr_multi_spec_provider_test", + target_under_test = ":asr_multi_spec", +) + +feat_req_multi_spec_provider_test( + name = "feat_req_multi_spec_provider_test", + target_under_test = ":feat_req_multi_spec", +) + +comp_req_multi_spec_provider_test( + name = "comp_req_multi_spec_provider_test", + target_under_test = ":comp_req_multi_spec", +) + +# Integration target: TRLC file using types from both specs; trlc --verify +# runs on build, confirming both RSL files are reachable at parse time. +assumed_system_requirements( + name = "asr_multi_spec_inttest", + srcs = ["fixtures/multi_spec/multi_spec.trlc"], + spec = [":extra_spec_fixture"], +) + +requirements_multi_spec_test_suite(name = "requirements_multi_spec_tests") + # ============================================================================ # lobster_config.bzl unit tests (format_lobster_sources / format_lobster_block) # ============================================================================ @@ -1464,6 +1527,7 @@ test_suite( name = "all_tests", tests = [ ":lobster_config_tests", + ":requirements_multi_spec_tests", ":requirements_rst_tests", ":seooc_tests", ":sphinx_module_tests", diff --git a/bazel/rules/rules_score/test/fixtures/multi_spec/extra_model.rsl b/bazel/rules/rules_score/test/fixtures/multi_spec/extra_model.rsl new file mode 100644 index 00000000..50739d6e --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/multi_spec/extra_model.rsl @@ -0,0 +1,22 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +// Project-specific model extension (fixture for multi-spec tests). +// Uses a distinct package name — TRLC does not allow two RSL files with the +// same package declaration. +package ProjectReq + +type ProjectNote "A project-specific note for internal tracking." { + description "Note content." + String +} diff --git a/bazel/rules/rules_score/test/fixtures/multi_spec/multi_spec.trlc b/bazel/rules/rules_score/test/fixtures/multi_spec/multi_spec.trlc new file mode 100644 index 00000000..dbeb5bb7 --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/multi_spec/multi_spec.trlc @@ -0,0 +1,31 @@ +/******************************************************************************** + * 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 fixture: uses AssumedSystemReq (base model) and ProjectNote +// (extra model) in one file to verify that both RSL files are reachable when +// spec is passed as a list. +package MultiSpecIntTest + +import ScoreReq +import ProjectReq + +ScoreReq.AssumedSystemReq asr_multi_spec_inttest_001 { + description = "The system shall support multi-spec requirements models." + safety = ScoreReq.Asil.B + version = 1 + rationale = "Validates that the base spec types are accessible when spec is a list." +} + +ProjectReq.ProjectNote note_multi_spec_inttest_001 { + description = "Validates that a type from the extra spec is accessible alongside the base model." +} diff --git a/bazel/rules/rules_score/test/fixtures/spec_extras/BUILD b/bazel/rules/rules_score/test/fixtures/spec_extras/BUILD new file mode 100644 index 00000000..844c86bb --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/spec_extras/BUILD @@ -0,0 +1,28 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", "component_requirements") +load("@trlc//:trlc.bzl", "trlc_specification") + +package(default_visibility = ["//visibility:public"]) + +trlc_specification( + name = "extra_spec", + srcs = ["extra_model.rsl"], +) + +component_requirements( + name = "spec_extras_comp_req", + srcs = ["project_requirements.trlc"], + spec_extras = [":extra_spec"], +) diff --git a/bazel/rules/rules_score/test/fixtures/spec_extras/extra_model.rsl b/bazel/rules/rules_score/test/fixtures/spec_extras/extra_model.rsl new file mode 100644 index 00000000..0abb20ad --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/spec_extras/extra_model.rsl @@ -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 + ********************************************************************************/ + +package ExtraModel +import ScoreReq + +type ProjectSpecificReq extends ScoreReq.CompReq { + project_id "Project identifier" String +} diff --git a/bazel/rules/rules_score/test/fixtures/spec_extras/project_requirements.trlc b/bazel/rules/rules_score/test/fixtures/spec_extras/project_requirements.trlc new file mode 100644 index 00000000..4decca6b --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/spec_extras/project_requirements.trlc @@ -0,0 +1,32 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +package TestFixtures + +import ExtraModel +import ScoreReq + +ScoreReq.AssumedSystemReq ASR_TEST_001 { + description = "Input system-level requirement for spec_extras test" + rationale = "Needed to satisfy derived_from constraints" + safety = ScoreReq.Asil.B + version = 1 +} + +ExtraModel.ProjectSpecificReq PROJ_REQ_001 { + description = "Requirement using a type from spec_extras." + safety = ScoreReq.Asil.B + derived_from = [ASR_TEST_001@1] + version = 1 + project_id = "TEST" +} diff --git a/bazel/rules/rules_score/test/requirements_multi_spec_test.bzl b/bazel/rules/rules_score/test/requirements_multi_spec_test.bzl new file mode 100644 index 00000000..21ab4e9f --- /dev/null +++ b/bazel/rules/rules_score/test/requirements_multi_spec_test.bzl @@ -0,0 +1,112 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +""" +Tests that requirement macros accept a list of labels for ``spec``. + +Verifies that assumed_system_requirements, feature_requirements, and +component_requirements all produce their expected providers when spec is +passed as a list of two labels. +""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load( + "@score_tooling//bazel/rules/rules_score:providers.bzl", + "AssumedSystemRequirementsInfo", + "ComponentRequirementsInfo", + "FeatureRequirementsInfo", + "SphinxSourcesInfo", +) + +# ============================================================================ +# assumed_system_requirements – list spec +# ============================================================================ + +def _asr_multi_spec_provider_test_impl(ctx): + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + asserts.true( + env, + AssumedSystemRequirementsInfo in target_under_test, + "assumed_system_requirements with list spec should provide AssumedSystemRequirementsInfo", + ) + asserts.true( + env, + SphinxSourcesInfo in target_under_test, + "assumed_system_requirements with list spec should provide SphinxSourcesInfo", + ) + return analysistest.end(env) + +asr_multi_spec_provider_test = analysistest.make(_asr_multi_spec_provider_test_impl) + +# ============================================================================ +# feature_requirements – list spec +# ============================================================================ + +def _feat_req_multi_spec_provider_test_impl(ctx): + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + asserts.true( + env, + FeatureRequirementsInfo in target_under_test, + "feature_requirements with list spec should provide FeatureRequirementsInfo", + ) + asserts.true( + env, + SphinxSourcesInfo in target_under_test, + "feature_requirements with list spec should provide SphinxSourcesInfo", + ) + return analysistest.end(env) + +feat_req_multi_spec_provider_test = analysistest.make(_feat_req_multi_spec_provider_test_impl) + +# ============================================================================ +# component_requirements – list spec +# ============================================================================ + +def _comp_req_multi_spec_provider_test_impl(ctx): + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + asserts.true( + env, + ComponentRequirementsInfo in target_under_test, + "component_requirements with list spec should provide ComponentRequirementsInfo", + ) + asserts.true( + env, + SphinxSourcesInfo in target_under_test, + "component_requirements with list spec should provide SphinxSourcesInfo", + ) + return analysistest.end(env) + +comp_req_multi_spec_provider_test = analysistest.make(_comp_req_multi_spec_provider_test_impl) + +# ============================================================================ +# Test Suite +# ============================================================================ + +def requirements_multi_spec_test_suite(name): + """Register all multi-spec requirement tests. + + Args: + name: Name for the test_suite target. + """ + native.test_suite( + name = name, + tests = [ + ":asr_multi_spec_provider_test", + ":feat_req_multi_spec_provider_test", + ":comp_req_multi_spec_provider_test", + # Integration test: trlc --verify with both RSL files actually loaded + ":asr_multi_spec_inttest_test", + ], + ) diff --git a/bazel/rules/rules_score/test/requirements_spec_extras_test.bzl b/bazel/rules/rules_score/test/requirements_spec_extras_test.bzl new file mode 100644 index 00000000..e26aeaee --- /dev/null +++ b/bazel/rules/rules_score/test/requirements_spec_extras_test.bzl @@ -0,0 +1,46 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +"""Analysis tests for spec_extras support in requirements macros.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("@trlc//:trlc.bzl", "TrlcProviderInfo") + +def _spec_extras_in_provider_test_impl(ctx): + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + spec_basenames = [f.basename for f in target_under_test[TrlcProviderInfo].spec.to_list()] + + asserts.true( + env, + "score_requirements_model.rsl" in spec_basenames, + "Base score spec should still be present in TrlcProviderInfo.spec", + ) + asserts.true( + env, + "extra_model.rsl" in spec_basenames, + "spec_extras RSL should be merged into TrlcProviderInfo.spec", + ) + + return analysistest.end(env) + +spec_extras_in_provider_test = analysistest.make(_spec_extras_in_provider_test_impl) + +def requirements_spec_extras_test_suite(name): + native.test_suite( + name = name, + tests = [ + ":spec_extras_in_provider_test", + ], + )