Skip to content

Commit 8a95cc5

Browse files
hoe-jocastler
authored andcommitted
dependable_element: symlink ancillary files and split requirements sections
Split the 'requirements' attribute handling into separate feature_requirements and assumed_system_requirements sections, accepting both FeatureRequirementsInfo and AssumedSystemRequirementsInfo providers on the requirements attr. Restructure the index template accordingly: - 'Assumed System' section: assumed_system_requirements + assumptions_of_use - 'Software Architectural Level' section: feature_requirements + architectural_design + dependability_analysis - Components are now referenced directly in the outer toctree (no intermediate components/index.rst)
1 parent 1512b54 commit 8a95cc5

3 files changed

Lines changed: 80 additions & 65 deletions

File tree

bazel/rules/rules_score/private/dependable_element.bzl

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,21 @@ def _process_artifact_files(ctx, artifact_name, label):
280280
.replace(".md", "")
281281
index_refs.append(doc_ref)
282282

283+
# Symlink ancillary files (present for sub-toctrees / .. uml:: resolution,
284+
# but NOT added to the outer toctree index).
285+
if SphinxSourcesInfo in label:
286+
for anc_file in label[SphinxSourcesInfo].ancillary.to_list():
287+
if anc_file.extension not in ["rst", "md", "puml", "plantuml", "png", "svg", "inc", "json"]:
288+
continue
289+
relative_path = _compute_relative_path(anc_file, _find_common_directory([anc_file]))
290+
output_file = _create_artifact_symlink(
291+
ctx,
292+
artifact_name,
293+
anc_file,
294+
relative_path,
295+
)
296+
output_files.append(output_file)
297+
283298
return (output_files, index_refs)
284299

285300
def _process_artifact_type(ctx, artifact_name):
@@ -686,10 +701,8 @@ def _dependable_element_index_impl(ctx):
686701

687702
# Process each well-known artifact type into symlinked output files and
688703
# toctree references for the index template.
689-
# "requirements" covers both feature_requirements and component_requirements.
690704
artifact_types = [
691705
"assumptions_of_use",
692-
"requirements",
693706
"architectural_design",
694707
"dependability_analysis",
695708
"checklists",
@@ -701,6 +714,24 @@ def _dependable_element_index_impl(ctx):
701714
output_files.extend(files)
702715
artifacts_by_type[artifact_name] = refs
703716

717+
# Collect feature_requirements refs from requirements targets that
718+
# carry FeatureRequirementsInfo.
719+
feature_req_refs = []
720+
for req_target in ctx.attr.requirements:
721+
if FeatureRequirementsInfo in req_target:
722+
label_files, label_refs = _process_artifact_files(ctx, "feature_requirements", req_target)
723+
output_files.extend(label_files)
724+
feature_req_refs.extend(label_refs)
725+
726+
# Collect assumed_system_requirements refs from requirements targets that
727+
# carry AssumedSystemRequirementsInfo.
728+
assumed_system_req_refs = []
729+
for req_target in ctx.attr.requirements:
730+
if AssumedSystemRequirementsInfo in req_target:
731+
label_files, label_refs = _process_artifact_files(ctx, "assumed_system_requirements", req_target)
732+
output_files.extend(label_files)
733+
assumed_system_req_refs.extend(label_refs)
734+
704735
# Collect all units recursively from components
705736
all_units = _collect_units_recursive(ctx.attr.components)
706737

@@ -746,18 +777,11 @@ def _dependable_element_index_impl(ctx):
746777
if CertifiedScope in dep:
747778
collected_certified_scopes.append(dep[CertifiedScope].transitive_scopes)
748779

749-
# Generate an intermediate components/index.rst that groups all component pages
750-
# under a single "Components" navigation entry in the Sphinx sidebar.
780+
# Reference component pages directly in the outer toctree, avoiding an
781+
# intermediate components/index.rst that would repeat "Components" in the
782+
# Sphinx sidebar navigation.
751783
if component_refs:
752-
comp_index_rst = ctx.actions.declare_file(ctx.label.name + "/components/index.rst")
753-
comp_index_underline = "=" * len("Components")
754-
comp_toctree_entries = "\n ".join(component_refs)
755-
ctx.actions.write(
756-
output = comp_index_rst,
757-
content = "Components\n" + comp_index_underline + "\n\n.. toctree::\n :maxdepth: 2\n\n " + comp_toctree_entries + "\n",
758-
)
759-
output_files.append(comp_index_rst)
760-
components_ref = "components/index"
784+
components_ref = "\n ".join(["components/" + name for name in component_refs])
761785
else:
762786
components_ref = ""
763787

@@ -775,8 +799,9 @@ def _dependable_element_index_impl(ctx):
775799
"{title}": title,
776800
"{underline}": underline,
777801
"{components}": components_ref,
802+
"{assumed_system_requirements}": "\n ".join(assumed_system_req_refs),
778803
"{assumptions_of_use}": "\n ".join(artifacts_by_type["assumptions_of_use"]),
779-
"{requirements}": "\n ".join(artifacts_by_type["requirements"]),
804+
"{feature_requirements}": "\n ".join(feature_req_refs),
780805
"{architectural_design}": "\n ".join(artifacts_by_type["architectural_design"]),
781806
"{dependability_analysis}": "\n ".join(artifacts_by_type["dependability_analysis"]),
782807
"{checklists}": "\n ".join(artifacts_by_type["checklists"]),
@@ -1000,8 +1025,8 @@ _dependable_element_index = rule(
10001025
),
10011026
"requirements": attr.label_list(
10021027
mandatory = True,
1003-
providers = [FeatureRequirementsInfo],
1004-
doc = "Feature requirements targets (feature_requirements only).",
1028+
providers = [[FeatureRequirementsInfo], [AssumedSystemRequirementsInfo]],
1029+
doc = "Feature or assumed system requirements targets.",
10051030
),
10061031
"architectural_design": attr.label_list(
10071032
mandatory = True,

bazel/rules/rules_score/templates/dependable_element_index.template.rst

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,25 @@
1515
Dependable element: {title}
1616
===================={underline}
1717

18-
Architectural Design
19-
--------------------
20-
21-
.. toctree::
22-
:maxdepth: 2
23-
24-
{architectural_design}
25-
26-
27-
Assumptions of Use
28-
------------------
18+
Assumed System
19+
--------------
2920

3021
.. toctree::
3122
:maxdepth: 2
3223

24+
{assumed_system_requirements}
3325
{assumptions_of_use}
3426

35-
Requirements
36-
------------
27+
Software Architectural Level
28+
----------------------------
3729

3830
.. toctree::
3931
:maxdepth: 2
4032

41-
{requirements}
33+
{feature_requirements}
34+
{architectural_design}
35+
{dependability_analysis}
36+
4237

4338
Components
4439
----------
@@ -49,16 +44,6 @@ Components
4944
{components}
5045

5146

52-
53-
54-
Dependability Analysis
55-
----------------------
56-
57-
.. toctree::
58-
:maxdepth: 2
59-
60-
{dependability_analysis}
61-
6247
Checklists
6348
----------
6449

bazel/rules/rules_score/test/seooc_test.bzl

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -119,46 +119,51 @@ seooc_needs_provider_test = analysistest.make(
119119
# ============================================================================
120120
# Regression tests: correct index.rst resolution (multi-index scenario)
121121
#
122-
# These tests guard against a bug where _score_html_impl scanned all relocated
123-
# source files for any path ending with "/index.rst" and used the last match.
124-
# A dependable_element with components generates both:
125-
# <name>/index.rst (root – correct Sphinx entry point)
126-
# <name>/components/index.rst (sub-page – must NOT be the entry point)
127-
# The old code would pick the sub-page as the Sphinx entry point.
122+
# These tests guard against a bug where the wrong index.rst was picked as the
123+
# Sphinx entry point when multiple index.rst files are present in the output
124+
# tree (e.g. unit/index.rst, component/index.rst alongside the root index.rst).
125+
# The fix is to carry the correct File explicitly via SphinxIndexFileInfo rather
126+
# than scanning all output files for any path ending with "/index.rst".
128127
# ============================================================================
129128

130129
def _seooc_multi_index_files_exist_test_impl(ctx):
131130
"""
132-
Given a dependable_element with at least one component,
133-
When the _index rule generates its output file set,
134-
Then both a root index.rst and a components/index.rst must be present,
135-
confirming the fixture exercises the multi-index scenario.
131+
Given a dependable_element with at least one component (which generates
132+
multiple .rst files in subdirectories alongside the root index.rst),
133+
When the _index rule finishes,
134+
Then SphinxIndexFileInfo must be provided and its index_file must be the
135+
root index.rst (not inside any subdirectory), confirming the correct
136+
entry point is propagated explicitly rather than discovered by scanning.
136137
"""
137138
env = analysistest.begin(ctx)
138139
target_under_test = analysistest.target_under_test(env)
139140

140-
# When: collect all output paths
141-
files = target_under_test[DefaultInfo].files.to_list()
142-
paths = [f.path for f in files]
141+
# Then: SphinxIndexFileInfo provider must be present
142+
asserts.true(
143+
env,
144+
SphinxIndexFileInfo in target_under_test,
145+
"Expected SphinxIndexFileInfo provider to be present on the index target",
146+
)
143147

144-
# Then: a components/index.rst sub-page exists
145-
components_index_paths = [p for p in paths if p.endswith("components/index.rst")]
148+
index_file = target_under_test[SphinxIndexFileInfo].index_file
149+
150+
# Then: the index file must be named index.rst
146151
asserts.true(
147152
env,
148-
len(components_index_paths) >= 1,
149-
"Expected a components/index.rst to be generated (fixture must have at least one component)",
153+
index_file.basename == "index.rst",
154+
"SphinxIndexFileInfo.index_file must be named index.rst, got: " + index_file.basename,
150155
)
151156

152-
# Then: a root index.rst (not inside components/) also exists
153-
root_index_paths = [
154-
p
155-
for p in paths
156-
if p.endswith("index.rst") and "components/" not in p
157-
]
157+
# Then: the index file must be at the root of the output tree (no subdirectory in dirname)
158+
# e.g. .../test_dependable_element_index/index.rst — the last path component of dirname
159+
# must equal the target name, not a sub-page like "components" or "units".
160+
parent = index_file.dirname.split("/")[-1]
158161
asserts.true(
159162
env,
160-
len(root_index_paths) >= 1,
161-
"Expected a root index.rst (outside components/) to be generated",
163+
parent == ctx.attr.target_under_test.label.name,
164+
"SphinxIndexFileInfo.index_file must sit directly under the target output dir, " +
165+
"not in a subdirectory. dirname ends in '" + parent + "', expected '" +
166+
ctx.attr.target_under_test.label.name + "'",
162167
)
163168

164169
return analysistest.end(env)

0 commit comments

Comments
 (0)