@@ -102,31 +102,30 @@ def filtered_needs_json(
102102 name ,
103103 src ,
104104 types = [],
105- components = [],
106- component_attr = "component" ,
105+ names = [],
107106 visibility = None ):
108107 """Extract a subset of sphinx-needs elements from a needs.json file.
109108
110109 Produces a `<name>.json` file containing only the needs that match all of
111110 the given filters. This is useful to hand a downstream consumer just the
112- elements (e.g. `feat_req`) of one or more particular components.
111+ elements (e.g. `feat_req`) of one or more particular features/ components.
113112
114113 Args:
115114 name: Name of the generated target. The output file is `<name>.json`.
116115 src: Label of a `needs_json` build output (a directory containing
117116 `needs.json`), e.g. `":needs_json"` or `"@score_process//:needs_json"`.
118117 types: Optional list of sphinx-needs element types to keep
119118 (e.g. `["feat_req", "comp_req"]`). If empty, all types are kept.
120- components : Optional list of component names to keep. If empty, all
121- components are kept.
122- component_attr: Need attribute matched against `components`.
123- Defaults to `"component"` .
119+ names : Optional list of feature/ component names to keep, matched against
120+ the second `__`-separated segment of each need ID (the
121+ `<type>__<name>__...` naming convention). If empty, all
122+ features/components are kept .
124123 visibility: Standard Bazel visibility for the generated target.
125124 """
126125 filter_tool = Label ("//scripts_bazel:filter_needs_json" )
127126
128127 type_args = " " .join (["--type '%s'" % t for t in types ])
129- component_args = " " .join (["--component '%s'" % c for c in components ])
128+ name_args = " " .join (["--name '%s'" % n for n in names ])
130129
131130 native .genrule (
132131 name = name ,
@@ -135,15 +134,13 @@ def filtered_needs_json(
135134 cmd = """
136135 $(location {filter_tool}) \
137136 --output $@ \
138- --component-attr '{component_attr}' \
139137 {type_args} \
140- {component_args } \
138+ {name_args } \
141139 $(location {src})/needs.json
142140 """ .format (
143141 filter_tool = filter_tool ,
144- component_attr = component_attr ,
145142 type_args = type_args ,
146- component_args = component_args ,
143+ name_args = name_args ,
147144 src = src ,
148145 ),
149146 tools = [filter_tool ],
@@ -165,16 +162,16 @@ def component_requirements(
165162 src: Label of a `needs_json` build output. Defaults to the calling
166163 package's `//:needs_json`.
167164 component: Optional component name. If given, only component requirements
168- tagged with that component are kept; if omitted, all component
169- requirements are kept.
165+ named with that component (per the `<type>__<name>__...`
166+ convention) are kept; if omitted, all component requirements are
167+ kept.
170168 visibility: Standard Bazel visibility for the generated target.
171169 """
172170 filtered_needs_json (
173171 name = name ,
174172 src = src ,
175173 types = ["comp_req" ],
176- components = [component ] if component else [],
177- component_attr = "tags" ,
174+ names = [component ] if component else [],
178175 visibility = visibility ,
179176 )
180177
@@ -193,16 +190,15 @@ def feature_requirements(
193190 src: Label of a `needs_json` build output. Defaults to the calling
194191 package's `//:needs_json`.
195192 feature: Optional feature name. If given, only feature requirements
196- tagged with that feature are kept; if omitted, all feature
197- requirements are kept.
193+ named with that feature (per the `<type>__<name>__...` convention)
194+ are kept; if omitted, all feature requirements are kept.
198195 visibility: Standard Bazel visibility for the generated target.
199196 """
200197 filtered_needs_json (
201198 name = name ,
202199 src = src ,
203200 types = ["feat_req" ],
204- components = [feature ] if feature else [],
205- component_attr = "tags" ,
201+ names = [feature ] if feature else [],
206202 visibility = visibility ,
207203 )
208204
@@ -221,16 +217,15 @@ def assumptions_of_use(
221217 src: Label of a `needs_json` build output. Defaults to the calling
222218 package's `//:needs_json`.
223219 component: Optional component name. If given, only assumptions of use
224- tagged with that component are kept; if omitted, all assumptions of
225- use are kept.
220+ named with that component (per the `<type>__<name>__...`
221+ convention) are kept; if omitted, all assumptions of use are kept.
226222 visibility: Standard Bazel visibility for the generated target.
227223 """
228224 filtered_needs_json (
229225 name = name ,
230226 src = src ,
231227 types = ["aou_req" ],
232- components = [component ] if component else [],
233- component_attr = "tags" ,
228+ names = [component ] if component else [],
234229 visibility = visibility ,
235230 )
236231
@@ -251,16 +246,16 @@ def feature_architecture(
251246 src: Label of a `needs_json` build output. Defaults to the calling
252247 package's `//:needs_json`.
253248 feature: Optional feature name. If given, only feature architecture
254- elements tagged with that feature are kept; if omitted, all feature
255- architecture elements are kept.
249+ elements named with that feature (per the `<type>__<name>__...`
250+ convention) are kept; if omitted, all feature architecture elements
251+ are kept.
256252 visibility: Standard Bazel visibility for the generated target.
257253 """
258254 filtered_needs_json (
259255 name = name ,
260256 src = src ,
261257 types = ["feat_arc_sta" , "feat_arc_dyn" ],
262- components = [feature ] if feature else [],
263- component_attr = "tags" ,
258+ names = [feature ] if feature else [],
264259 visibility = visibility ,
265260 )
266261
@@ -281,16 +276,16 @@ def component_architecture(
281276 src: Label of a `needs_json` build output. Defaults to the calling
282277 package's `//:needs_json`.
283278 component: Optional component name. If given, only component architecture
284- elements tagged with that component are kept; if omitted, all
285- component architecture elements are kept.
279+ elements named with that component (per the `<type>__<name>__...`
280+ convention) are kept; if omitted, all component architecture
281+ elements are kept.
286282 visibility: Standard Bazel visibility for the generated target.
287283 """
288284 filtered_needs_json (
289285 name = name ,
290286 src = src ,
291287 types = ["comp_arc_sta" , "comp_arc_dyn" ],
292- components = [component ] if component else [],
293- component_attr = "tags" ,
288+ names = [component ] if component else [],
294289 visibility = visibility ,
295290 )
296291
@@ -386,14 +381,26 @@ def requirements_checklist(
386381 checklist_id ,
387382 deps ,
388383 src = "//:needs_json" ,
384+ link_fields = ["derived_from" , "satisfies" , "covers" ],
385+ extra_needs = [],
389386 visibility = None ):
390387 """Validate a requirement checklist (`req_chklst`) against its build output.
391388
392- Building this target recomputes the SHA256 over the concatenated outputs of
393- `deps` and compares it to the `sha256` attribute of the `req_chklst` need
394- `checklist_id` (looked up in `src`'s `needs.json`). The build **fails** when
395- the hashes differ, i.e. when a validated target output has changed since the
396- checklist was last reviewed.
389+ Building this target recomputes the SHA256 over the requirements in `deps`
390+ **and**, by default, over everything they depend on transitively, and
391+ compares it to the `sha256` attribute of the `req_chklst` need `checklist_id`
392+ (looked up in `src`'s `needs.json`). The build **fails** when the hashes
393+ differ, i.e. when a validated requirement *or one of its (recursive)
394+ dependencies* has changed since the checklist was last reviewed.
395+
396+ The dependency graph is the sphinx-needs link graph: starting from the
397+ requirements in `deps` (the *roots*), the `link_fields` (by default
398+ `derived_from`, `satisfies` and `covers`) are followed recursively through
399+ `src`'s `needs.json`. For feature requirements this means the linked
400+ stakeholder requirements (and their parents in turn) are part of the hash, so
401+ changing a relevant stakeholder requirement makes this checklist go out of
402+ date. Pass `link_fields = []` to restore the old behaviour of hashing only
403+ the requirements in `deps`.
397404
398405 Typical usage validates the extracted requirements of a component against the
399406 checklist that reviewed them:
@@ -418,30 +425,50 @@ def requirements_checklist(
418425 name: Name of the generated target. The output file is `<name>.sha256`.
419426 checklist_id: Id of the `req_chklst` need to validate
420427 (e.g. `"req_chklst__bitmanipulation__comp_req"`).
421- deps: List of labels whose outputs are hashed and validated. Usually a
422- single `component_requirements`/`filtered_needs_json` target.
423- src: Label of a `needs_json` build output containing the checklist need.
424- Defaults to the calling package's `//:needs_json`.
428+ deps: List of labels whose outputs define the root requirements that are
429+ hashed and validated. Usually a single
430+ `component_requirements`/`feature_requirements`/`filtered_needs_json`
431+ target.
432+ src: Label of a `needs_json` build output containing the checklist need
433+ and the full link graph. Defaults to the calling package's
434+ `//:needs_json`.
435+ link_fields: Sphinx-needs link fields followed recursively from the root
436+ requirements to include their (transitive) dependencies in the hash.
437+ Defaults to `["derived_from", "satisfies", "covers"]`. Set to `[]` to
438+ hash only the requirements in `deps`.
439+ extra_needs: Optional list of additional `needs_json` build outputs that
440+ provide the full content of needs referenced from the validated
441+ requirements but not contained in `src` (e.g. stakeholder
442+ requirements imported from an upstream repository). Without them such
443+ external needs are hashed as `<MISSING>` and changes to them are not
444+ detected. Typically the same upstream `needs_json` targets passed as
445+ `data` to `docs(...)` (e.g. `["@score_platform//:needs_json"]`).
425446 visibility: Standard Bazel visibility for the generated target.
426447 """
427448 validate_tool = Label ("//scripts_bazel:validate_checklist" )
428449
429450 dep_args = " " .join (["$(locations %s)" % d for d in deps ])
451+ link_args = " " .join (["--link-field '%s'" % f for f in link_fields ])
452+ extra_args = " " .join (["--extra-needs-json $(location %s)/needs.json" % e for e in extra_needs ])
430453
431454 native .genrule (
432455 name = name ,
433- srcs = [src ] + deps ,
456+ srcs = [src ] + extra_needs + deps ,
434457 outs = [name + ".sha256" ],
435458 cmd = """
436459 $(location {validate_tool}) \
437460 --needs-json $(location {src})/needs.json \
438461 --checklist-id '{checklist_id}' \
439462 --output $@ \
463+ {link_args} \
464+ {extra_args} \
440465 {dep_args}
441466 """ .format (
442467 validate_tool = validate_tool ,
443468 checklist_id = checklist_id ,
444469 src = src ,
470+ link_args = link_args ,
471+ extra_args = extra_args ,
445472 dep_args = dep_args ,
446473 ),
447474 tools = [validate_tool ],
@@ -453,14 +480,27 @@ def architecture_checklist(
453480 checklist_id ,
454481 deps ,
455482 src = "//:needs_json" ,
483+ link_fields = ["fulfils" , "includes" , "uses" , "provides" , "derived_from" , "satisfies" , "covers" ],
484+ extra_needs = [],
456485 visibility = None ):
457486 """Validate an architecture checklist (`arch_chklst`) against its build output.
458487
459- Building this target recomputes the SHA256 over the concatenated outputs of
460- `deps` and compares it to the `sha256` attribute of the `arch_chklst` need
461- `checklist_id` (looked up in `src`'s `needs.json`). The build **fails** when
462- the hashes differ, i.e. when a validated target output has changed since the
463- checklist was last reviewed.
488+ Building this target recomputes the SHA256 over the architecture in `deps`
489+ **and**, by default, over everything they depend on transitively, and
490+ compares it to the `sha256` attribute of the `arch_chklst` need `checklist_id`
491+ (looked up in `src`'s `needs.json`). The build **fails** when the hashes
492+ differ, i.e. when a validated architecture element *or one of its (recursive)
493+ dependencies* has changed since the checklist was last reviewed.
494+
495+ The dependency graph is the sphinx-needs link graph: starting from the
496+ architecture elements in `deps` (the *roots*), the `link_fields` are followed
497+ recursively through `src`'s `needs.json`. The defaults follow the structural
498+ architecture links (`includes`, `uses`, `provides`) as well as `fulfils` and
499+ the requirement links (`derived_from`, `satisfies`, `covers`), so the closure
500+ reaches the fulfilled requirements and their parents. Changing a fulfilled
501+ requirement (or a stakeholder requirement it derives from) therefore makes
502+ this checklist go out of date. Pass `link_fields = []` to restore the old
503+ behaviour of hashing only the elements in `deps`.
464504
465505 Typical usage validates the extracted architecture of a component against the
466506 checklist that reviewed it:
@@ -485,31 +525,50 @@ def architecture_checklist(
485525 name: Name of the generated target. The output file is `<name>.sha256`.
486526 checklist_id: Id of the `arch_chklst` need to validate
487527 (e.g. `"arch_chklst__bitmanipulation__comp_arc"`).
488- deps: List of labels whose outputs are hashed and validated. Usually a
489- single `feature_architecture`/`component_architecture`/
490- `filtered_needs_json` target.
491- src: Label of a `needs_json` build output containing the checklist need.
492- Defaults to the calling package's `//:needs_json`.
528+ deps: List of labels whose outputs define the root architecture elements
529+ that are hashed and validated. Usually a single
530+ `feature_architecture`/`component_architecture`/`filtered_needs_json`
531+ target.
532+ src: Label of a `needs_json` build output containing the checklist need
533+ and the full link graph. Defaults to the calling package's
534+ `//:needs_json`.
535+ link_fields: Sphinx-needs link fields followed recursively from the root
536+ architecture elements to include their (transitive) dependencies in
537+ the hash. Defaults to the structural architecture links plus the
538+ requirement links. Set to `[]` to hash only the elements in `deps`.
539+ extra_needs: Optional list of additional `needs_json` build outputs that
540+ provide the full content of needs referenced from the validated
541+ architecture elements but not contained in `src` (e.g. feature
542+ requirements imported from an upstream repository). Without them such
543+ external needs are hashed as `<MISSING>` and changes to them are not
544+ detected. Typically the same upstream `needs_json` targets passed as
545+ `data` to `docs(...)` (e.g. `["@score_platform//:needs_json"]`).
493546 visibility: Standard Bazel visibility for the generated target.
494547 """
495548 validate_tool = Label ("//scripts_bazel:validate_checklist" )
496549
497550 dep_args = " " .join (["$(locations %s)" % d for d in deps ])
551+ link_args = " " .join (["--link-field '%s'" % f for f in link_fields ])
552+ extra_args = " " .join (["--extra-needs-json $(location %s)/needs.json" % e for e in extra_needs ])
498553
499554 native .genrule (
500555 name = name ,
501- srcs = [src ] + deps ,
556+ srcs = [src ] + extra_needs + deps ,
502557 outs = [name + ".sha256" ],
503558 cmd = """
504559 $(location {validate_tool}) \
505560 --needs-json $(location {src})/needs.json \
506561 --checklist-id '{checklist_id}' \
507562 --output $@ \
563+ {link_args} \
564+ {extra_args} \
508565 {dep_args}
509566 """ .format (
510567 validate_tool = validate_tool ,
511568 checklist_id = checklist_id ,
512569 src = src ,
570+ link_args = link_args ,
571+ extra_args = extra_args ,
513572 dep_args = dep_args ,
514573 ),
515574 tools = [validate_tool ],
0 commit comments