1 | # SPDX-License-Identifier: BSD-2-Clause |
---|
2 | """ Functions for application configuration documentation generation. """ |
---|
3 | |
---|
4 | # Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de) |
---|
5 | # |
---|
6 | # Redistribution and use in source and binary forms, with or without |
---|
7 | # modification, are permitted provided that the following conditions |
---|
8 | # are met: |
---|
9 | # 1. Redistributions of source code must retain the above copyright |
---|
10 | # notice, this list of conditions and the following disclaimer. |
---|
11 | # 2. Redistributions in binary form must reproduce the above copyright |
---|
12 | # notice, this list of conditions and the following disclaimer in the |
---|
13 | # documentation and/or other materials provided with the distribution. |
---|
14 | # |
---|
15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
---|
16 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
18 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
---|
19 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
---|
20 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
---|
21 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
---|
22 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
23 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
---|
24 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
---|
25 | # POSSIBILITY OF SUCH DAMAGE. |
---|
26 | |
---|
27 | import copy |
---|
28 | from typing import Any, Dict, List, Optional |
---|
29 | |
---|
30 | from rtemsspec.content import CContent, get_value_double_colon, \ |
---|
31 | get_value_doxygen_function, get_value_hash |
---|
32 | from rtemsspec.sphinxcontent import SphinxContent, SphinxMapper |
---|
33 | from rtemsspec.items import EmptyItem, Item, ItemCache, ItemGetValueContext, \ |
---|
34 | ItemMapper |
---|
35 | |
---|
36 | ItemMap = Dict[str, Item] |
---|
37 | |
---|
38 | _FEATURE = "This configuration option is a boolean feature define." |
---|
39 | |
---|
40 | _OPTION_TYPES = { |
---|
41 | "feature": _FEATURE, |
---|
42 | "feature-enable": _FEATURE, |
---|
43 | "integer": "This configuration option is an integer define.", |
---|
44 | "initializer": "This configuration option is an initializer define." |
---|
45 | } |
---|
46 | |
---|
47 | _OPTION_DEFAULT_CONFIG = { |
---|
48 | "feature": |
---|
49 | lambda item: item["default"], |
---|
50 | "feature-enable": |
---|
51 | lambda item: |
---|
52 | """If this configuration option is undefined, then the described feature is not |
---|
53 | enabled.""" |
---|
54 | } |
---|
55 | |
---|
56 | |
---|
57 | class _ContentAdaptor: |
---|
58 | """ |
---|
59 | The content adaptor provides a specialized interface to a content class. |
---|
60 | |
---|
61 | By default, Sphinx content is generated. |
---|
62 | """ |
---|
63 | def __init__(self, mapper: ItemMapper, content: Any) -> None: |
---|
64 | self.mapper = mapper |
---|
65 | self.content = content |
---|
66 | |
---|
67 | def substitute(self, text: Optional[str]) -> str: |
---|
68 | """ Substitutes the optional text using the item mapper. """ |
---|
69 | return self.mapper.substitute(text) |
---|
70 | |
---|
71 | def add_group(self, name: str, description: str) -> None: |
---|
72 | """ Adds an option group. """ |
---|
73 | self.content.add_header(name, level=2) |
---|
74 | self.content.add(description) |
---|
75 | |
---|
76 | def add_option(self, name: str, index_entries: List[str]) -> None: |
---|
77 | """ Adds an option. """ |
---|
78 | self.content.add_index_entries([name] + index_entries) |
---|
79 | self.content.add_label(name) |
---|
80 | self.content.add_header(name, level=3) |
---|
81 | self.content.add_definition_item("CONSTANT:", f"``{name}``") |
---|
82 | |
---|
83 | def add_option_type(self, option_type: str) -> None: |
---|
84 | """ Adds an option type. """ |
---|
85 | self.content.add_definition_item("OPTION TYPE:", option_type) |
---|
86 | |
---|
87 | def add_option_default_value(self, value: str) -> None: |
---|
88 | """ Adds an option default value. """ |
---|
89 | self.content.add_definition_item("DEFAULT VALUE:", value) |
---|
90 | |
---|
91 | def add_option_default_config(self, config: str) -> None: |
---|
92 | """ Adds an option default configuration. """ |
---|
93 | self.content.add_definition_item("DEFAULT CONFIGURATION:", config) |
---|
94 | |
---|
95 | def add_option_value_constraints(self, lines: List[str]) -> None: |
---|
96 | """ Adds a option value constraints. """ |
---|
97 | self.content.add_definition_item("VALUE CONSTRAINTS:", lines) |
---|
98 | |
---|
99 | def add_option_description(self, description: str) -> None: |
---|
100 | """ Adds a option description. """ |
---|
101 | self.content.add_definition_item("DESCRIPTION:", description) |
---|
102 | |
---|
103 | def add_option_notes(self, notes: Optional[str]) -> None: |
---|
104 | """ Adds option notes. """ |
---|
105 | if not notes: |
---|
106 | notes = "None." |
---|
107 | self.content.add_definition_item("NOTES:", notes) |
---|
108 | |
---|
109 | def add_licence_and_copyrights(self) -> None: |
---|
110 | """ Adds the license and copyrights. """ |
---|
111 | self.content.add_licence_and_copyrights() |
---|
112 | |
---|
113 | def register_license_and_copyrights_of_item(self, item: Item) -> None: |
---|
114 | """ Registers the license and copyrights of the item. """ |
---|
115 | self.content.register_license_and_copyrights_of_item(item) |
---|
116 | |
---|
117 | def write(self, filename: str): |
---|
118 | """ Writes the content to the file specified by the path. """ |
---|
119 | self.content.write(filename) |
---|
120 | |
---|
121 | |
---|
122 | class _SphinxContentAdaptor(_ContentAdaptor): |
---|
123 | def __init__(self, mapper: ItemMapper) -> None: |
---|
124 | super().__init__(mapper, SphinxContent()) |
---|
125 | |
---|
126 | |
---|
127 | class _DoxygenContentAdaptor(_ContentAdaptor): |
---|
128 | # pylint: disable=attribute-defined-outside-init |
---|
129 | |
---|
130 | def __init__(self, mapper: ItemMapper) -> None: |
---|
131 | super().__init__(mapper, CContent()) |
---|
132 | self._reset() |
---|
133 | |
---|
134 | def _reset(self) -> None: |
---|
135 | self._name = "" |
---|
136 | self._option_type = "" |
---|
137 | self._default_value = "" |
---|
138 | self._default_config = "" |
---|
139 | self._value_constraints = [] # type: List[str] |
---|
140 | self._description = "" |
---|
141 | |
---|
142 | def add_group(self, name: str, description: str) -> None: |
---|
143 | identifier = f"RTEMSApplConfig{name.replace(' ', '')}" |
---|
144 | with self.content.defgroup_block(identifier, name): |
---|
145 | self.content.add("@ingroup RTEMSApplConfig") |
---|
146 | self.content.doxyfy(description) |
---|
147 | self.content.add("@{") |
---|
148 | |
---|
149 | def add_option(self, name: str, _index_entries: List[str]) -> None: |
---|
150 | self.content.open_doxygen_block() |
---|
151 | self._name = name |
---|
152 | |
---|
153 | def add_option_type(self, option_type: str) -> None: |
---|
154 | self._option_type = option_type |
---|
155 | |
---|
156 | def add_option_default_value(self, value: str) -> None: |
---|
157 | self._default_value = value |
---|
158 | |
---|
159 | def add_option_default_config(self, config: str) -> None: |
---|
160 | self._default_config = config |
---|
161 | |
---|
162 | def add_option_value_constraints(self, lines: List[str]) -> None: |
---|
163 | self._value_constraints = lines |
---|
164 | |
---|
165 | def add_option_description(self, description: str) -> None: |
---|
166 | self._description = description |
---|
167 | |
---|
168 | def add_option_notes(self, notes: Optional[str]) -> None: |
---|
169 | self.content.add_brief_description(self._option_type) |
---|
170 | self.content.doxyfy(self._description) |
---|
171 | self.content.add_paragraph("Default Value", self._default_value) |
---|
172 | self.content.add_paragraph("Default Configuration", |
---|
173 | self._default_config) |
---|
174 | self.content.add_paragraph("Value Constraints", |
---|
175 | self._value_constraints) |
---|
176 | self.content.add_paragraph("Notes", notes) |
---|
177 | self.content.close_comment_block() |
---|
178 | self.content.append(f"#define {self._name}") |
---|
179 | self._reset() |
---|
180 | |
---|
181 | def add_licence_and_copyrights(self) -> None: |
---|
182 | self.content.add("/** @} */") |
---|
183 | |
---|
184 | |
---|
185 | def _generate_feature(content: _ContentAdaptor, item: Item, |
---|
186 | option_type: str) -> None: |
---|
187 | content.add_option_default_config( |
---|
188 | content.substitute(_OPTION_DEFAULT_CONFIG[option_type](item))) |
---|
189 | |
---|
190 | |
---|
191 | def _generate_min_max(lines: List[str], value: str, word: str) -> None: |
---|
192 | lines.append("The value of this configuration option shall be " |
---|
193 | f"{word} than or equal to {value}.") |
---|
194 | |
---|
195 | |
---|
196 | def _generate_set(lines: List[str], values: List[Any]) -> None: |
---|
197 | value_set = "{" + ", ".join([str(x) for x in values]) + "}" |
---|
198 | lines.append("The value of this configuration option shall be") |
---|
199 | lines.append(f"an element of {value_set}.") |
---|
200 | |
---|
201 | |
---|
202 | def _start_constraint_list(lines: List[str]) -> None: |
---|
203 | lines.append("The value of this configuration option shall " |
---|
204 | "satisfy all of the following") |
---|
205 | lines.append("constraints:") |
---|
206 | |
---|
207 | |
---|
208 | def _generate_item_min(lines: List[str], constraints: Dict[str, Any]) -> None: |
---|
209 | if "min" in constraints: |
---|
210 | value = constraints["min"] |
---|
211 | lines.append("") |
---|
212 | lines.append(f"* It shall be greater than or equal to {value}.") |
---|
213 | |
---|
214 | |
---|
215 | def _generate_item_max(lines: List[str], constraints: Dict[str, Any]) -> None: |
---|
216 | if "max" in constraints: |
---|
217 | value = constraints["max"] |
---|
218 | lines.append("") |
---|
219 | lines.append(f"* It shall be less than or equal to {value}.") |
---|
220 | |
---|
221 | |
---|
222 | def _generate_item_set(lines: List[str], constraints: Dict[str, Any]) -> None: |
---|
223 | if "set" in constraints: |
---|
224 | value_set = constraints["set"] |
---|
225 | lines.append("") |
---|
226 | lines.append(f"* It shall be an element of {value_set}.") |
---|
227 | |
---|
228 | |
---|
229 | def _generate_item_texts(lines: List[str], constraints: Dict[str, |
---|
230 | Any]) -> None: |
---|
231 | for text in constraints.get("texts", []): |
---|
232 | lines.append("") |
---|
233 | text = text.replace("The value of this configuration option", "It") |
---|
234 | text = text.strip().split("\n") |
---|
235 | lines.append(f"* {text[0]}") |
---|
236 | lines.extend([f" {x}" if x else "" for x in text[1:]]) |
---|
237 | |
---|
238 | |
---|
239 | def _resolve_constraint_links(content: _ContentAdaptor, item: Item, |
---|
240 | constraints: Dict[str, Any]) -> None: |
---|
241 | texts = [] # type: List[str] |
---|
242 | for parent in item.parents("constraint"): |
---|
243 | content.register_license_and_copyrights_of_item(parent) |
---|
244 | texts.append(parent["text"]) |
---|
245 | if texts: |
---|
246 | constraints.setdefault("texts", []).extend(reversed(texts)) |
---|
247 | |
---|
248 | |
---|
249 | def _generate_constraint(content: _ContentAdaptor, item: Item) -> None: |
---|
250 | constraints = copy.deepcopy(item["constraints"]) |
---|
251 | _resolve_constraint_links(content, item, constraints) |
---|
252 | lines = [] # type: List[str] |
---|
253 | count = len(constraints) |
---|
254 | if count == 1: |
---|
255 | if "min" in constraints: |
---|
256 | _generate_min_max(lines, constraints["min"], "greater") |
---|
257 | elif "max" in constraints: |
---|
258 | _generate_min_max(lines, constraints["max"], "less") |
---|
259 | elif "set" in constraints: |
---|
260 | _generate_set(lines, constraints["set"]) |
---|
261 | elif "texts" in constraints: |
---|
262 | if len(constraints["texts"]) == 1: |
---|
263 | lines.extend(constraints["texts"][0].strip().split("\n")) |
---|
264 | else: |
---|
265 | _start_constraint_list(lines) |
---|
266 | _generate_item_texts(lines, constraints) |
---|
267 | elif count == 2 and "min" in constraints and "max" in constraints: |
---|
268 | minimum = constraints["min"] |
---|
269 | maximum = constraints["max"] |
---|
270 | lines.append("The value of this configuration option shall be " |
---|
271 | f"greater than or equal to {minimum}") |
---|
272 | lines.append(f"and less than or equal to {maximum}.") |
---|
273 | else: |
---|
274 | _start_constraint_list(lines) |
---|
275 | _generate_item_min(lines, constraints) |
---|
276 | _generate_item_max(lines, constraints) |
---|
277 | _generate_item_set(lines, constraints) |
---|
278 | _generate_item_texts(lines, constraints) |
---|
279 | content.add_option_value_constraints( |
---|
280 | [content.substitute(line) for line in lines]) |
---|
281 | |
---|
282 | |
---|
283 | def _generate_initializer_or_integer(content: _ContentAdaptor, item: Item, |
---|
284 | _option_type: str) -> None: |
---|
285 | default_value = item["default-value"] |
---|
286 | if not isinstance(default_value, str) or " " not in default_value: |
---|
287 | default_value = f"The default value is {default_value}." |
---|
288 | content.add_option_default_value(content.substitute(default_value)) |
---|
289 | _generate_constraint(content, item) |
---|
290 | |
---|
291 | |
---|
292 | _OPTION_GENERATORS = { |
---|
293 | "feature": _generate_feature, |
---|
294 | "feature-enable": _generate_feature, |
---|
295 | "initializer": _generate_initializer_or_integer, |
---|
296 | "integer": _generate_initializer_or_integer |
---|
297 | } |
---|
298 | |
---|
299 | |
---|
300 | def _generate(group: Item, options: ItemMap, content: _ContentAdaptor) -> None: |
---|
301 | content.register_license_and_copyrights_of_item(group) |
---|
302 | content.add_group(group["name"], content.substitute(group["description"])) |
---|
303 | for item in sorted(options.values(), key=lambda x: x["name"]): |
---|
304 | content.mapper.item = item |
---|
305 | name = item["name"] |
---|
306 | content.register_license_and_copyrights_of_item(item) |
---|
307 | content.add_option(name, item["index-entries"]) |
---|
308 | option_type = item["appl-config-option-type"] |
---|
309 | content.add_option_type(_OPTION_TYPES[option_type]) |
---|
310 | _OPTION_GENERATORS[option_type](content, item, option_type) |
---|
311 | content.add_option_description(content.substitute(item["description"])) |
---|
312 | content.add_option_notes(content.substitute(item["notes"])) |
---|
313 | content.add_licence_and_copyrights() |
---|
314 | |
---|
315 | |
---|
316 | def _get_value_none(_ctx: ItemGetValueContext) -> Any: |
---|
317 | return None |
---|
318 | |
---|
319 | |
---|
320 | def _sphinx_ref(ref: str) -> str: |
---|
321 | return f":ref:`{ref}`" |
---|
322 | |
---|
323 | |
---|
324 | _PTHREAD_NAME_NP = "http://man7.org/linux/man-pages/man3/" \ |
---|
325 | "pthread_setname_np.3.html" |
---|
326 | |
---|
327 | _SPHINX_DOC_REFS = { |
---|
328 | "config-scheduler-clustered": |
---|
329 | _sphinx_ref("ConfigurationSchedulersClustered"), |
---|
330 | "config-scheduler-table": _sphinx_ref("ConfigurationSchedulerTable"), |
---|
331 | "config-unlimited-objects": _sphinx_ref("ConfigUnlimitedObjects"), |
---|
332 | "mp-proxies": _sphinx_ref("MPCIProxies"), |
---|
333 | "mrsp": _sphinx_ref("MrsP"), |
---|
334 | "pthread-setname-np": f"`PTHREAD_SETNAME_NP(3) <{_PTHREAD_NAME_NP}>`_", |
---|
335 | "scheduler-cbs": _sphinx_ref("SchedulerCBS"), |
---|
336 | "scheduler-concepts": _sphinx_ref("SchedulingConcepts"), |
---|
337 | "scheduler-edf": _sphinx_ref("SchedulerEDF"), |
---|
338 | "scheduler-priority": _sphinx_ref("SchedulerPriority"), |
---|
339 | "scheduler-priority-simple": _sphinx_ref("SchedulerPrioritySimple"), |
---|
340 | "scheduler-smp-edf": _sphinx_ref("SchedulerSMPEDF"), |
---|
341 | "scheduler-smp-priority-affinity": |
---|
342 | _sphinx_ref("SchedulerSMPPriorityAffinity"), |
---|
343 | "scheduler-smp-priority": _sphinx_ref("SchedulerSMPPriority"), |
---|
344 | "scheduler-smp-priority-simple": _sphinx_ref("SchedulerSMPPrioritySimple"), |
---|
345 | "terminate": _sphinx_ref("Terminate"), |
---|
346 | } |
---|
347 | |
---|
348 | |
---|
349 | def _get_value_sphinx_reference(ctx: ItemGetValueContext) -> Any: |
---|
350 | return _SPHINX_DOC_REFS[ctx.key] |
---|
351 | |
---|
352 | |
---|
353 | def _get_value_sphinx_function(ctx: ItemGetValueContext) -> Any: |
---|
354 | return f"``{ctx.value[ctx.key]}()``" |
---|
355 | |
---|
356 | |
---|
357 | def _get_value_sphinx_code(ctx: ItemGetValueContext) -> Any: |
---|
358 | return f"``{ctx.value[ctx.key]}``" |
---|
359 | |
---|
360 | |
---|
361 | def _add_sphinx_get_values(mapper: ItemMapper) -> None: |
---|
362 | for key in _SPHINX_DOC_REFS: |
---|
363 | for opt in ["feature-enable", "feature", "initializer", "integer"]: |
---|
364 | doc_ref = f"interface/appl-config-option/{opt}:/document-reference" |
---|
365 | mapper.add_get_value(doc_ref, _get_value_none) |
---|
366 | mapper.add_get_value(f"{doc_ref}/{key}", |
---|
367 | _get_value_sphinx_reference) |
---|
368 | mapper.add_get_value("interface/function:/name", |
---|
369 | _get_value_sphinx_function) |
---|
370 | mapper.add_get_value("interface/macro:/name", _get_value_sphinx_function) |
---|
371 | mapper.add_get_value("interface/struct:/name", _get_value_sphinx_code) |
---|
372 | mapper.add_get_value("interface/typedef:/name", _get_value_sphinx_code) |
---|
373 | mapper.add_get_value("interface/union:/name", _get_value_sphinx_code) |
---|
374 | |
---|
375 | |
---|
376 | def _c_user_ref(ref: str, name: str) -> str: |
---|
377 | c_user = "https://docs.rtems.org/branches/master/c-user/" |
---|
378 | return f"<a href={c_user}{ref}>{name}</a>" |
---|
379 | |
---|
380 | |
---|
381 | _DOXYGEN_DOC_REFS = { |
---|
382 | "config-scheduler-clustered": |
---|
383 | _c_user_ref("config/scheduler-clustered.html", |
---|
384 | "Clustered Scheduler Configuration"), |
---|
385 | "config-scheduler-table": |
---|
386 | _c_user_ref( |
---|
387 | "config/scheduler-clustered.html#configuration-step-3-scheduler-table", |
---|
388 | "Configuration Step 3 - Scheduler Table"), |
---|
389 | "config-unlimited-objects": |
---|
390 | _c_user_ref("config/intro.html#unlimited-objects", "Unlimited Objects"), |
---|
391 | "mp-proxies": |
---|
392 | _c_user_ref("multiprocessing.html#proxies", "Proxies"), |
---|
393 | "mrsp": |
---|
394 | _c_user_ref( |
---|
395 | "key_concepts.html#multiprocessor-resource-sharing-protocol-mrsp", |
---|
396 | "Multiprocessor Resource Sharing Protocol (MrsP)"), |
---|
397 | "pthread-setname-np": |
---|
398 | f"<a href={_PTHREAD_NAME_NP}>PTHREAD_SETNAME_NP(3)</a>", |
---|
399 | "scheduler-cbs": |
---|
400 | _c_user_ref( |
---|
401 | "scheduling_concepts.html#constant-bandwidth-server-scheduling-cbs", |
---|
402 | "Constant Bandwidth Server Scheduling (CBS)"), |
---|
403 | "scheduler-concepts": |
---|
404 | _c_user_ref("scheduling_concepts.html", "Scheduling Concepts"), |
---|
405 | "scheduler-edf": |
---|
406 | _c_user_ref("scheduling_concepts.html#earliest-deadline-first-scheduler", |
---|
407 | "Earliest Deadline First Scheduler"), |
---|
408 | "scheduler-priority": |
---|
409 | _c_user_ref("scheduling_concepts.html#deterministic-priority-scheduler", |
---|
410 | "Deterministic Priority Scheduler"), |
---|
411 | "scheduler-priority-simple": |
---|
412 | _c_user_ref("scheduling_concepts.html#simple-priority-scheduler", |
---|
413 | "Simple Priority Scheduler"), |
---|
414 | "scheduler-smp-edf": |
---|
415 | _c_user_ref( |
---|
416 | "scheduling_concepts.html#earliest-deadline-first-smp-scheduler", |
---|
417 | "Earliest Deadline First SMP Scheduler"), |
---|
418 | "scheduler-smp-priority-affinity": |
---|
419 | _c_user_ref( |
---|
420 | "scheduling_concepts.html" |
---|
421 | "#arbitrary-processor-affinity-priority-smp-scheduler", |
---|
422 | "Arbitrary Processor Affinity Priority SMP Scheduler"), |
---|
423 | "scheduler-smp-priority": |
---|
424 | _c_user_ref( |
---|
425 | "scheduling_concepts.html#deterministic-priority-smp-scheduler", |
---|
426 | "Deterministic Priority SMP Scheduler"), |
---|
427 | "scheduler-smp-priority-simple": |
---|
428 | _c_user_ref("scheduling_concepts.html#simple-priority-smp-scheduler", |
---|
429 | "Simple Priority SMP Scheduler"), |
---|
430 | "terminate": |
---|
431 | _c_user_ref("fatal_error.html#announcing-a-fatal-error", |
---|
432 | "Announcing a Fatal Error"), |
---|
433 | } |
---|
434 | |
---|
435 | |
---|
436 | def _get_value_doxygen_reference(ctx: ItemGetValueContext) -> Any: |
---|
437 | return _DOXYGEN_DOC_REFS[ctx.key] |
---|
438 | |
---|
439 | |
---|
440 | def _add_doxygen_get_values(mapper: ItemMapper) -> None: |
---|
441 | for key in _DOXYGEN_DOC_REFS: |
---|
442 | for opt in ["feature-enable", "feature", "initializer", "integer"]: |
---|
443 | doc_ref = f"interface/appl-config-option/{opt}:/document-reference" |
---|
444 | mapper.add_get_value(doc_ref, _get_value_none) |
---|
445 | mapper.add_get_value(f"{doc_ref}/{key}", |
---|
446 | _get_value_doxygen_reference) |
---|
447 | name = f"interface/appl-config-option/{opt}:/name" |
---|
448 | mapper.add_get_value(name, get_value_hash) |
---|
449 | mapper.add_get_value("interface/function:/name", |
---|
450 | get_value_doxygen_function) |
---|
451 | mapper.add_get_value("interface/macro:/name", get_value_doxygen_function) |
---|
452 | mapper.add_get_value("interface/struct:/name", get_value_double_colon) |
---|
453 | mapper.add_get_value("interface/typedef:/name", get_value_double_colon) |
---|
454 | mapper.add_get_value("interface/union:/name", get_value_double_colon) |
---|
455 | |
---|
456 | |
---|
457 | def generate(config: dict, item_cache: ItemCache) -> None: |
---|
458 | """ |
---|
459 | Generates application configuration documentation sources according to the |
---|
460 | configuration. |
---|
461 | |
---|
462 | :param config: A dictionary with configuration entries. |
---|
463 | :param item_cache: The specification item cache containing the application |
---|
464 | configuration groups and options. |
---|
465 | """ |
---|
466 | sphinx_mapper = SphinxMapper(EmptyItem()) |
---|
467 | _add_sphinx_get_values(sphinx_mapper) |
---|
468 | doxygen_mapper = ItemMapper(EmptyItem()) |
---|
469 | _add_doxygen_get_values(doxygen_mapper) |
---|
470 | doxygen_content = _DoxygenContentAdaptor(doxygen_mapper) |
---|
471 | with doxygen_content.content.defgroup_block( |
---|
472 | "RTEMSApplConfig", "Application Configuration Options"): |
---|
473 | doxygen_content.content.add("@ingroup RTEMSAPI") |
---|
474 | for group_config in config["groups"]: |
---|
475 | group = item_cache[group_config["uid"]] |
---|
476 | assert group.type == "interface/appl-config-group" |
---|
477 | options = {} # type: ItemMap |
---|
478 | for child in group.children("appl-config-group-member"): |
---|
479 | assert child.type.startswith("interface/appl-config-option") |
---|
480 | options[child.uid] = child |
---|
481 | sphinx_content = _SphinxContentAdaptor(sphinx_mapper) |
---|
482 | _generate(group, options, sphinx_content) |
---|
483 | sphinx_content.write(group_config["target"]) |
---|
484 | _generate(group, options, doxygen_content) |
---|
485 | doxygen_content.content.prepend_copyrights_and_licenses() |
---|
486 | doxygen_content.content.prepend_spdx_license_identifier() |
---|
487 | doxygen_content.write(config["doxygen-target"]) |
---|