diff --git a/bazel/rules/rules_score/lobster/config/lobster_de.conf.tpl b/bazel/rules/rules_score/lobster/config/lobster_de.conf.tpl index b4ccfd6b..dd264cdf 100644 --- a/bazel/rules/rules_score/lobster/config/lobster_de.conf.tpl +++ b/bazel/rules/rules_score/lobster/config/lobster_de.conf.tpl @@ -2,6 +2,10 @@ requirements "Feature Requirements" { {FEAT_REQ_SOURCES} } +requirements "System Requirements" { +{SYSTEM_REQ_SOURCES} +} + requirements "Forwarded AoUs" { {FORWARDED_AOU_SOURCES} } diff --git a/bazel/rules/rules_score/private/component.bzl b/bazel/rules/rules_score/private/component.bzl index 1d38e02f..cd9800de 100644 --- a/bazel/rules/rules_score/private/component.bzl +++ b/bazel/rules/rules_score/private/component.bzl @@ -76,6 +76,7 @@ def _component_impl(ctx): req_lobster_files.append(req_target[ComponentRequirementsInfo].srcs) if FeatureRequirementsInfo in req_target: feat_req_lobster_files.append(req_target[FeatureRequirementsInfo].srcs) + feat_req_lobster_files.append(req_target[FeatureRequirementsInfo].system_req_srcs) if AssumedSystemRequirementsInfo in req_target: feat_req_lobster_files.append(req_target[AssumedSystemRequirementsInfo].srcs) diff --git a/bazel/rules/rules_score/private/dependable_element.bzl b/bazel/rules/rules_score/private/dependable_element.bzl index 6536efd8..62d2de30 100644 --- a/bazel/rules/rules_score/private/dependable_element.bzl +++ b/bazel/rules/rules_score/private/dependable_element.bzl @@ -1080,6 +1080,11 @@ def _dependable_element_index_impl(ctx): # Build the DE-level lobster report if feature and component traces exist feat_req_list = feat_req_lobster_depset.to_list() + system_req_depsets = [] + for req_target in ctx.attr.requirements: + if FeatureRequirementsInfo in req_target: + system_req_depsets.append(req_target[FeatureRequirementsInfo].system_req_srcs) + system_req_list = depset(transitive = system_req_depsets).to_list() comp_req_list = comp_req_lobster_depset.to_list() comp_test_list = comp_test_lobster_depset.to_list() comp_arch_list = comp_arch_lobster_depset.to_list() @@ -1149,6 +1154,7 @@ def _dependable_element_index_impl(ctx): output = lobster_config, substitutions = { "{FEAT_REQ_SOURCES}": format_lobster_sources(feat_req_list), + "{SYSTEM_REQ_SOURCES}": format_lobster_sources(system_req_list), "{FORWARDED_AOU_SOURCES}": format_lobster_sources(received_aou_list), "{COMP_REQ_SOURCES}": format_lobster_sources(comp_req_list), "{COMP_REQ_TRACE}": comp_req_trace_lines, @@ -1161,7 +1167,7 @@ def _dependable_element_index_impl(ctx): }, ) - all_lobster_inputs = feat_req_list + comp_req_list + comp_arch_list + comp_test_list + interface_req_list + fm_list + cm_list + rc_list + received_aou_list + all_lobster_inputs = feat_req_list + system_req_list + comp_req_list + comp_arch_list + comp_test_list + interface_req_list + fm_list + cm_list + rc_list + received_aou_list lobster_report_file = subrule_lobster_report(all_lobster_inputs, lobster_config) lobster_html_report = subrule_lobster_html_report(lobster_report_file) diff --git a/bazel/rules/rules_score/private/requirements.bzl b/bazel/rules/rules_score/private/requirements.bzl index 4645ba7d..ae4109a3 100644 --- a/bazel/rules/rules_score/private/requirements.bzl +++ b/bazel/rules/rules_score/private/requirements.bzl @@ -90,8 +90,13 @@ def _requirements_impl(ctx): # Build the kind-specific domain provider. # ------------------------------------------------------------------------- if ctx.attr.req_kind == "feature": + system_req_lobster_depsets = [] + for dep in ctx.attr.deps: + if AssumedSystemRequirementsInfo in dep: + system_req_lobster_depsets.append(dep[AssumedSystemRequirementsInfo].srcs) req_provider = FeatureRequirementsInfo( srcs = depset([lobster_file]), + system_req_srcs = depset(transitive = system_req_lobster_depsets), name = ctx.label.name, ) elif ctx.attr.req_kind == "component": diff --git a/bazel/rules/rules_score/providers.bzl b/bazel/rules/rules_score/providers.bzl index 0bf78451..b3f8b9c9 100644 --- a/bazel/rules/rules_score/providers.bzl +++ b/bazel/rules/rules_score/providers.bzl @@ -90,7 +90,8 @@ UnitInfo = provider( FeatureRequirementsInfo = provider( doc = "Provider for feature requirements artifacts.", fields = { - "srcs": "Depset of .lobster traceability files generated from TRLC requirement sources.", + "srcs": "Depset of .lobster traceability files generated from feature requirement TRLC sources.", + "system_req_srcs": "Depset of .lobster traceability files from system requirement deps (e.g. assumed system requirements). These are included in the dependable_element analysis so that derived_from references in feature requirements resolve correctly.", "name": "Name of the requirements target.", }, ) diff --git a/bazel/rules/rules_score/test/BUILD b/bazel/rules/rules_score/test/BUILD index e00a0c6d..bb70063f 100644 --- a/bazel/rules/rules_score/test/BUILD +++ b/bazel/rules/rules_score/test/BUILD @@ -77,6 +77,8 @@ load( ) load( ":seooc_test.bzl", + "de_lobster_config_includes_system_req_srcs_test", + "feat_req_system_req_srcs_populated_test", "seooc_all_sources_relocated_test", "seooc_artifacts_copied_test", "seooc_index_file_provider_test", @@ -576,6 +578,17 @@ seooc_index_file_provider_test( target_under_test = ":test_dependable_element_index", ) +# Regression tests: system requirements are propagated via system_req_srcs +feat_req_system_req_srcs_populated_test( + name = "seooc_tests_feat_req_system_req_srcs_populated", + target_under_test = ":feat_req", +) + +de_lobster_config_includes_system_req_srcs_test( + name = "seooc_tests_de_lobster_config_includes_system_req_srcs", + target_under_test = ":seooc_test_lib_index", +) + # ============================================================================ # Test Suites # ============================================================================ @@ -642,7 +655,9 @@ test_suite( tests = [ ":seooc_tests_all_sources_relocated", ":seooc_tests_artifacts_copied", + ":seooc_tests_de_lobster_config_includes_system_req_srcs", ":seooc_tests_dep_links_use_doc_dir", + ":seooc_tests_feat_req_system_req_srcs_populated", ":seooc_tests_index_file_provider", ":seooc_tests_index_generation", ":seooc_tests_multi_index_files_exist", diff --git a/bazel/rules/rules_score/test/seooc_test.bzl b/bazel/rules/rules_score/test/seooc_test.bzl index 689a62f1..28b3f999 100644 --- a/bazel/rules/rules_score/test/seooc_test.bzl +++ b/bazel/rules/rules_score/test/seooc_test.bzl @@ -21,7 +21,7 @@ Tests the SEooC (Safety Element out of Context) functionality including: """ load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("@score_tooling//bazel/rules/rules_score:providers.bzl", "SphinxIndexFileInfo", "SphinxModuleInfo", "SphinxNeedsInfo") +load("@score_tooling//bazel/rules/rules_score:providers.bzl", "FeatureRequirementsInfo", "SphinxIndexFileInfo", "SphinxModuleInfo", "SphinxNeedsInfo") def _seooc_index_generation_test_impl(ctx): """Test that dependable_element generates proper index.rst file.""" @@ -321,3 +321,82 @@ def _seooc_index_file_provider_test_impl(ctx): seooc_index_file_provider_test = analysistest.make( impl = _seooc_index_file_provider_test_impl, ) + +def _feat_req_system_req_srcs_populated_test_impl(ctx): + """ + Given a feature_requirements target whose deps include an assumed_system_requirements target, + When FeatureRequirementsInfo is inspected, + Then system_req_srcs must be non-empty and contain a .lobster file. + """ + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + asserts.true( + env, + FeatureRequirementsInfo in target_under_test, + "Expected FeatureRequirementsInfo to be provided by feature_requirements target", + ) + + info = target_under_test[FeatureRequirementsInfo] + system_req_files = info.system_req_srcs.to_list() + + asserts.true( + env, + len(system_req_files) > 0, + "Expected system_req_srcs to be non-empty when feature_requirements has assumed_system_requirements deps", + ) + + lobster_files = [f for f in system_req_files if f.extension == "lobster"] + asserts.true( + env, + len(lobster_files) > 0, + "Expected system_req_srcs to contain at least one .lobster file; got: " + str([f.basename for f in system_req_files]), + ) + + return analysistest.end(env) + +feat_req_system_req_srcs_populated_test = analysistest.make( + impl = _feat_req_system_req_srcs_populated_test_impl, +) + +def _de_lobster_config_includes_system_req_srcs_test_impl(ctx): + """ + Given a dependable_element whose feature_requirements deps include assumed_system_requirements, + When the lobster report action is inspected, + Then the system requirement lobster file must appear as an input to that action. + """ + env = analysistest.begin(ctx) + + actions = analysistest.target_actions(env) + + # Find the lobster report action: it takes de_traceability_config plus all + # lobster source files as inputs, and produces the lobster JSON report. + # Identify it by the presence of de_traceability_config in its inputs. + lobster_report_action = None + for action in actions: + input_basenames = [f.basename for f in action.inputs.to_list()] + if "de_traceability_config" in input_basenames: + lobster_report_action = action + break + + asserts.true( + env, + lobster_report_action != None, + "Expected to find the lobster report action (one whose inputs include de_traceability_config)", + ) + + input_basenames = [f.basename for f in lobster_report_action.inputs.to_list()] + lobster_inputs = [n for n in input_basenames if n.endswith(".lobster")] + + # The system requirement lobster file (from the asr_trlc fixture target) must be present + asserts.true( + env, + "asr_trlc.lobster" in lobster_inputs, + "Expected 'asr_trlc.lobster' in lobster report action inputs; got: " + str(lobster_inputs), + ) + + return analysistest.end(env) + +de_lobster_config_includes_system_req_srcs_test = analysistest.make( + impl = _de_lobster_config_includes_system_req_srcs_test_impl, +)