Skip to content
Merged
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
1 change: 1 addition & 0 deletions bazel/rules/rules_score/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ exports_files([
"templates/unit.template.rst",
"templates/component.template.rst",
"templates/fmea.template.rst",
"templates/puml_diagram.template.rst",
])

compile_pip_requirements(
Expand Down
22 changes: 20 additions & 2 deletions bazel/rules/rules_score/private/architectural_design.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ to produce FlatBuffers binary representations of the parsed diagrams.
"""

load("//bazel/rules/rules_score:providers.bzl", "ArchitecturalDesignInfo", "SphinxSourcesInfo")
load("//bazel/rules/rules_score/private:puml_utils.bzl", "make_puml_rst_wrappers")
load("//bazel/rules/rules_score/private:verbosity.bzl", "VERBOSITY_ATTR", "get_log_level")

# ============================================================================
Expand Down Expand Up @@ -149,6 +150,17 @@ def _architectural_design_impl(ctx):
transitive = [all_source_files],
)

# Generate a thin RST wrapper for every .puml diagram so it appears as a
# toctree entry in the dependable_element index.
rst_wrappers = make_puml_rst_wrappers(
ctx,
ctx.files.static + ctx.files.dynamic,
ctx.label.name,
ctx.file._puml_rst_template,
)

sphinx_srcs = depset(rst_wrappers, transitive = [sphinx_files])

return [
DefaultInfo(files = all_source_files),
ArchitecturalDesignInfo(
Expand All @@ -160,8 +172,9 @@ def _architectural_design_impl(ctx):
),
# Source diagram files + plantuml_links.json for the sphinx documentation build
SphinxSourcesInfo(
srcs = sphinx_files,
deps = sphinx_files,
srcs = sphinx_srcs,
deps = sphinx_srcs,
ancillary = depset(),
),
]

Expand Down Expand Up @@ -205,6 +218,11 @@ _architectural_design = rule(
cfg = "exec",
doc = "Tool that generates plantuml_links.json from FlatBuffers diagram outputs",
),
"_puml_rst_template": attr.label(
default = Label("//bazel/rules/rules_score:templates/puml_diagram.template.rst"),
allow_single_file = True,
doc = "RST template for PlantUML diagram wrapper pages.",
),
},
**VERBOSITY_ATTR
),
Expand Down
35 changes: 31 additions & 4 deletions bazel/rules/rules_score/private/assumptions_of_use.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,27 @@ def _assumptions_of_use_impl(ctx):
Returns:
List of providers including DefaultInfo and AssumptionsOfUseInfo
"""
srcs = depset(ctx.files.srcs)

# Render each TRLC source to RST for Sphinx
rendered_files = []
for src in ctx.attr.srcs:
trlc_provider = src[TrlcProviderInfo]
rendered_file = ctx.actions.declare_file("{}_{}.rst".format(ctx.attr.name, src.label.name))
args = ctx.actions.args()
args.add("--output", rendered_file.path)
args.add("--input-dir", ".")
args.add("--title", ctx.label.name.replace("_", " ").title())
args.add("--source-files")
args.add_all(trlc_provider.reqs)
ctx.actions.run(
inputs = src[DefaultInfo].files,
outputs = [rendered_file],
arguments = [args],
executable = ctx.executable._renderer,
)
rendered_files.append(rendered_file)

all_srcs = depset(rendered_files)

# Collect requirements providers and lobster files
reqs = []
Expand All @@ -55,20 +75,21 @@ def _assumptions_of_use_impl(ctx):
lobster_files.append(info.srcs)

# Collect transitive sphinx sources from requirements
transitive = [srcs]
transitive = [all_srcs]
for req in ctx.attr.requirements:
if SphinxSourcesInfo in req:
transitive.append(req[SphinxSourcesInfo].deps)

return [
DefaultInfo(files = srcs),
DefaultInfo(files = all_srcs),
AssumptionsOfUseInfo(
srcs = depset(transitive = lobster_files),
name = ctx.label.name,
),
SphinxSourcesInfo(
srcs = srcs,
srcs = all_srcs,
deps = depset(transitive = transitive),
ancillary = depset(),
),
]

Expand All @@ -90,6 +111,12 @@ _assumptions_of_use = rule(
mandatory = False,
doc = "List of feature or component requirements targets that these Assumptions of Use trace to",
),
"_renderer": attr.label(
default = Label("@trlc//tools/trlc_rst:trlc_rst"),
executable = True,
allow_files = True,
cfg = "exec",
),
},
)

Expand Down
1 change: 1 addition & 0 deletions bazel/rules/rules_score/private/component.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ def _component_impl(ctx):
SphinxSourcesInfo(
srcs = req_sphinx_depset,
deps = sphinx_depset,
ancillary = depset(),
),
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def _component_requirements_impl(ctx):
),
SphinxSourcesInfo(
srcs = srcs,
transitive_srcs = depset(transitive = transitive),
deps = depset(transitive = transitive),
ancillary = depset(),
),
]

Expand Down
20 changes: 12 additions & 8 deletions bazel/rules/rules_score/private/dependability_analysis.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,23 @@ load("//bazel/rules/rules_score/private:lobster_config.bzl", "format_lobster_sou
# Private Helpers
# ============================================================================

def _collect_analysis_providers(sa, rst_srcs_list, rst_deps_list, lobster_files):
def _collect_analysis_providers(sa, rst_srcs_list, rst_deps_list, rst_ancillary_list, lobster_files):
"""Collect analysis providers from a single sub-analysis target.

Updates the provided lists/dicts in-place.

Args:
sa: A sub-analysis target (fmea or security).
rst_srcs_list: List of depsets to extend with SphinxSourcesInfo.srcs.
rst_deps_list: List of depsets to extend with SphinxSourcesInfo.deps.
lobster_files: Dict to update with AnalysisInfo.lobster_files
(canonical name → File).
sa: A sub-analysis target (fmea or security).
rst_srcs_list: List of depsets to extend with SphinxSourcesInfo.srcs.
rst_deps_list: List of depsets to extend with SphinxSourcesInfo.deps.
rst_ancillary_list: List of depsets to extend with SphinxSourcesInfo.ancillary.
lobster_files: Dict to update with AnalysisInfo.lobster_files
(canonical name → File).
"""
if SphinxSourcesInfo in sa:
rst_srcs_list.append(sa[SphinxSourcesInfo].srcs)
rst_deps_list.append(sa[SphinxSourcesInfo].deps)
rst_ancillary_list.append(sa[SphinxSourcesInfo].ancillary)
if AnalysisInfo in sa:
lobster_files.update(sa[AnalysisInfo].lobster_files)

Expand Down Expand Up @@ -74,6 +76,7 @@ def _dependability_analysis_impl(ctx):

rst_srcs_transitive = [dfa_rst_files]
rst_deps_transitive = [dfa_rst_files]
rst_ancillary_transitive = []
lobster_files = {} # canonical name → File, merged from all sub-analyses

# -------------------------------------------------------------------------
Expand All @@ -82,15 +85,15 @@ def _dependability_analysis_impl(ctx):
fmea_output_files = []
for sa in ctx.attr.fmea:
fmea_output_files.append(sa[DefaultInfo].files)
_collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, lobster_files)
_collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, rst_ancillary_transitive, lobster_files)

# -------------------------------------------------------------------------
# Collect from security_analysis targets
# -------------------------------------------------------------------------
security_output_files = []
for sa in ctx.attr.security_analysis:
security_output_files.append(sa[DefaultInfo].files)
_collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, lobster_files)
_collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, rst_ancillary_transitive, lobster_files)

# Architectural design sphinx deps (optional)
if ctx.attr.arch_design and SphinxSourcesInfo in ctx.attr.arch_design:
Expand Down Expand Up @@ -182,6 +185,7 @@ def _dependability_analysis_impl(ctx):
SphinxSourcesInfo(
srcs = all_rst_srcs,
deps = all_rst_deps,
ancillary = depset(transitive = rst_ancillary_transitive),
),
]

Expand Down
57 changes: 41 additions & 16 deletions bazel/rules/rules_score/private/dependable_element.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,21 @@ def _process_artifact_files(ctx, artifact_name, label):
.replace(".md", "")
index_refs.append(doc_ref)

# Symlink ancillary files (present for sub-toctrees / .. uml:: resolution,
# but NOT added to the outer toctree index).
if SphinxSourcesInfo in label:
for anc_file in label[SphinxSourcesInfo].ancillary.to_list():
if anc_file.extension not in ["rst", "md", "puml", "plantuml", "png", "svg", "inc", "json"]:
continue
relative_path = _compute_relative_path(anc_file, _find_common_directory([anc_file]))
output_file = _create_artifact_symlink(
ctx,
artifact_name,
anc_file,
relative_path,
)
output_files.append(output_file)

return (output_files, index_refs)

def _process_artifact_type(ctx, artifact_name):
Expand Down Expand Up @@ -686,10 +701,8 @@ def _dependable_element_index_impl(ctx):

# Process each well-known artifact type into symlinked output files and
# toctree references for the index template.
# "requirements" covers both feature_requirements and component_requirements.
artifact_types = [
"assumptions_of_use",
"requirements",
"architectural_design",
"dependability_analysis",
"checklists",
Expand All @@ -701,6 +714,24 @@ def _dependable_element_index_impl(ctx):
output_files.extend(files)
artifacts_by_type[artifact_name] = refs

# Collect feature_requirements refs from requirements targets that
# carry FeatureRequirementsInfo.
feature_req_refs = []
for req_target in ctx.attr.requirements:
if FeatureRequirementsInfo in req_target:
label_files, label_refs = _process_artifact_files(ctx, "feature_requirements", req_target)
output_files.extend(label_files)
feature_req_refs.extend(label_refs)

# Collect assumed_system_requirements refs from requirements targets that
# carry AssumedSystemRequirementsInfo.
assumed_system_req_refs = []
for req_target in ctx.attr.requirements:
if AssumedSystemRequirementsInfo in req_target:
label_files, label_refs = _process_artifact_files(ctx, "assumed_system_requirements", req_target)
output_files.extend(label_files)
assumed_system_req_refs.extend(label_refs)

# Collect all units recursively from components
all_units = _collect_units_recursive(ctx.attr.components)

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

# Generate an intermediate components/index.rst that groups all component pages
# under a single "Components" navigation entry in the Sphinx sidebar.
# Reference component pages directly in the outer toctree, avoiding an
# intermediate components/index.rst that would repeat "Components" in the
# Sphinx sidebar navigation.
if component_refs:
comp_index_rst = ctx.actions.declare_file(ctx.label.name + "/components/index.rst")
comp_index_underline = "=" * len("Components")
comp_toctree_entries = "\n ".join(component_refs)
ctx.actions.write(
output = comp_index_rst,
content = "Components\n" + comp_index_underline + "\n\n.. toctree::\n :maxdepth: 2\n\n " + comp_toctree_entries + "\n",
)
output_files.append(comp_index_rst)
components_ref = "components/index"
components_ref = "\n ".join(["components/" + name for name in component_refs])
else:
components_ref = ""

Expand All @@ -775,8 +799,9 @@ def _dependable_element_index_impl(ctx):
"{title}": title,
"{underline}": underline,
"{components}": components_ref,
"{assumed_system_requirements}": "\n ".join(assumed_system_req_refs),
"{assumptions_of_use}": "\n ".join(artifacts_by_type["assumptions_of_use"]),
"{requirements}": "\n ".join(artifacts_by_type["requirements"]),
"{feature_requirements}": "\n ".join(feature_req_refs),
"{architectural_design}": "\n ".join(artifacts_by_type["architectural_design"]),
"{dependability_analysis}": "\n ".join(artifacts_by_type["dependability_analysis"]),
"{checklists}": "\n ".join(artifacts_by_type["checklists"]),
Expand Down Expand Up @@ -1000,8 +1025,8 @@ _dependable_element_index = rule(
),
"requirements": attr.label_list(
mandatory = True,
providers = [FeatureRequirementsInfo],
doc = "Feature requirements targets (feature_requirements only).",
providers = [[FeatureRequirementsInfo], [AssumedSystemRequirementsInfo]],
doc = "Feature or assumed system requirements targets.",
),
"architectural_design": attr.label_list(
mandatory = True,
Expand Down
3 changes: 2 additions & 1 deletion bazel/rules/rules_score/private/feature_requirements.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ def _feature_requirements_impl(ctx):
),
SphinxSourcesInfo(
srcs = srcs,
transitive_srcs = srcs,
deps = srcs,
ancillary = depset(),
),
]

Expand Down
Loading
Loading