├── .gitignore ├── LICENSE ├── README.md ├── Spexyfile ├── deploy.bat ├── doc ├── DOC_MAN_SPX.pdf ├── Doxyfile ├── Doxyfile-PDF ├── img │ ├── spexygen-video.jpg │ ├── spx-code.jpg │ ├── spx-uid.jpg │ ├── spx-uid.webp │ ├── spx.odg │ ├── spx.svg │ ├── spx_flow.eps │ ├── spx_flow.svg │ ├── spx_tr.eps │ └── spx_tr.svg ├── main.dox ├── make.bat └── spex.json ├── example ├── Doxyfile ├── Doxyfile-PDF ├── inc │ └── header.h ├── main.dox ├── make.bat ├── spex.json ├── src │ └── source.c ├── srs.dox └── test │ ├── test1.c │ └── test2.c ├── setup.py ├── spexygen-awesome ├── doxygen-awesome-darkmode-toggle.js ├── doxygen-awesome-fragment-copy-button.js ├── doxygen-awesome-interactive-toc.js ├── doxygen-awesome-paragraph-link.js ├── doxygen-awesome-sidebar-only-darkmode-toggle.css ├── doxygen-awesome-sidebar-only.css ├── doxygen-awesome-tabs.js ├── doxygen-awesome.css ├── help.dox ├── image-preview.js ├── img │ ├── banner_spexygen.jpg │ ├── banner_spexygen.webp │ ├── help_dark.webp │ ├── help_light.webp │ ├── help_using.jpg │ ├── logo_spexygen.png │ ├── logo_spexygen.webp │ ├── tree-view_linked.webp │ └── tree-view_unlinked.webp ├── jquery.js ├── spexy-awesome.css ├── spexy-footer.html ├── spexy-footer.tex ├── spexy-header.html └── spexy-header.tex └── spexygen.py /.gitignore: -------------------------------------------------------------------------------- 1 | spexygen.tar.gz 2 | 3 | html/ 4 | spexy/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 - 2023 jothepro (doxygen-awesome) 4 | Copyright (c) 2024 Quantum Leaps, LLC 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Brought to you by: 2 | [![Quantum Leaps](https://www.state-machine.com/attachments/logo_ql_400.png)](https://www.state-machine.com) 3 |
4 | 5 | [![GitHub release (latest by date)](https://img.shields.io/github/v/release/QuantumLeaps/spexygen)](https://github.com/QuantumLeaps/spexygen/releases/latest) 6 | [![GitHub](https://img.shields.io/github/license/QuantumLeaps/spexygen)](https://github.com/QuantumLeaps/spexygen/blob/main/LICENSE) 7 | 8 |

9 | 10 | 11 |

12 | 13 | # Spexygen - Traceable Specifications Based on Doxygen 14 | _Spexygen_ documentation (generated with _Spexygen_) is available at: 15 | - [Spexygen Reference Manual in HTML](https://www.state-machine.com/spexygen) 16 | - [Spexygen Reference Manual in PDF](https://www.state-machine.com/spexygen/DOC_MAN_SPX.pdf) 17 | 18 |

19 | 20 |
21 | Traceable Documentation with Doxygen & Spexygen
22 |

23 | 24 | # Licensing 25 | Spexygen is released under the terms of the permissive [MIT open source license](LICENSE). 26 | Please note that the attribution clause in the MIT license requires you to preserve 27 | the original copyright notice in all changes and derivate works. 28 | 29 | # How to Help this Project? 30 | Please feel free to clone, fork, and make pull requests to improve **Spexygen**. 31 | If you like this project, please give it a star on GitHub (in the upper-right corner 32 | of your browser window): 33 | 34 |

35 | 36 |

37 | -------------------------------------------------------------------------------- /Spexyfile: -------------------------------------------------------------------------------- 1 | #============================================================================= 2 | # _Spexygen_ - Traceable Specifications Based on doxygen 3 | # Copyright (C) 2024 Quantum Leaps, LLC 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | # this software and associated documentation files (the "Software"), to deal in 9 | # the Software without restriction, including without limitation the rights to 10 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | # the Software, and to permit persons to whom the Software is furnished to do so, 12 | # subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | # 24 | # Contact information: 25 | # 26 | # 27 | #============================================================================= 28 | 29 | #--------------------------------------------------------------------------- 30 | # Project related configuration options 31 | #--------------------------------------------------------------------------- 32 | # Spexygen: Commands 33 | #--------------------------------------------------------------------------- 34 | ALIASES += \ 35 | "uid{2}=@addindex \1^^@subsection \1 \1^^\2^^" \ 36 | "uid_litem{1}=@par \1" \ 37 | "uid_bw_trace=@par Backward Traceability" \ 38 | "uid_bw_trace{1}=@par Backward Traceability" \ 39 | "uid_fw_trace=@par Forward Traceability" \ 40 | "uid_fw_trace{1}=@par Forward Traceability (truncated to \1 level(s))" \ 41 | "enduid=

" \ 42 | "code_uid{2}=@brief \2" \ 43 | "code_alias{2}=@anchor \1^^@brief \1 : \2" \ 44 | "code_litem{1}=@par \1" \ 45 | "code_bw_trace=@par Backward Traceability" \ 46 | "code_bw_trace{1}=@par Backward Traceability" \ 47 | "code_fw_trace=@par Forward Traceability" \ 48 | "code_fw_trace{1}=@par Forward Traceability (truncated to \1 level(s))" \ 49 | "endcode_uid=" \ 50 | "tr{1}=@ref \1 \"\1\"" \ 51 | "precondition{2}=@par Precondition `\1:\2`" \ 52 | "postcondition{2}=@par Postcondition `\1:\2`" \ 53 | "invariant{2}=@par Invariant `\1:\2`" \ 54 | "nav{2}=@ref \1@ref \2" \ 55 | "nav_prev{1}=@ref \1" \ 56 | "nav_next{1}=@ref \1" \ 57 | "next{1}=@ref \1" \ 58 | "caption{1}=
\1
" \ 59 | "webref{2}=\2↑" \ 60 | "clearpage=@latexonly \clearpage @endlatexonly" 61 | 62 | #--------------------------------------------------------------------------- 63 | # Configuration options related to the HTML output 64 | #--------------------------------------------------------------------------- 65 | # Spexygen: spexygen-awesome style 66 | #--------------------------------------------------------------------------- 67 | HTML_EXTRA_STYLESHEET = $(SPEXYGEN)/spexygen-awesome/doxygen-awesome.css \ 68 | $(SPEXYGEN)/spexygen-awesome/doxygen-awesome-sidebar-only.css \ 69 | $(SPEXYGEN)/spexygen-awesome/doxygen-awesome-sidebar-only-darkmode-toggle.css \ 70 | $(SPEXYGEN)/spexygen-awesome/spexy-awesome.css 71 | HTML_EXTRA_FILES = $(SPEXYGEN)/spexygen-awesome/doxygen-awesome-darkmode-toggle.js \ 72 | $(SPEXYGEN)/spexygen-awesome/doxygen-awesome-fragment-copy-button.js \ 73 | $(SPEXYGEN)/spexygen-awesome/doxygen-awesome-paragraph-link.js \ 74 | $(SPEXYGEN)/spexygen-awesome/image-preview.js 75 | 76 | #--------------------------------------------------------------------------- 77 | # Spexygen: customized HTML header/footer 78 | # (can be overridden by the including Doxyfile) 79 | #--------------------------------------------------------------------------- 80 | HTML_HEADER = $(SPEXYGEN)/spexygen-awesome/spexy-header.html 81 | HTML_FOOTER = $(SPEXYGEN)/spexygen-awesome/spexy-footer.html 82 | 83 | #--------------------------------------------------------------------------- 84 | # Configuration options related to the LaTeX output 85 | #--------------------------------------------------------------------------- 86 | # Spexygen: customized LaTeX header/footer 87 | # (can be overridden by the including Doxyfile) 88 | #--------------------------------------------------------------------------- 89 | LATEX_HEADER = $(SPEXYGEN)/spexygen-awesome/spexy-header.tex 90 | LATEX_FOOTER = $(SPEXYGEN)/spexygen-awesome/spexy-footer.tex 91 | -------------------------------------------------------------------------------- /deploy.bat: -------------------------------------------------------------------------------- 1 | :: Batch file for building the Python package, checking it, 2 | :: and uploading it to PyPi. 3 | :: 4 | :: usage: 5 | :: make 6 | :: 7 | @setlocal 8 | 9 | :: set the project 10 | @set PROJ=spexygen 11 | 12 | :: adjust the Python location for your system 13 | @set PYTHON=python 14 | 15 | :: cleanup any previous builds... 16 | @rmdir /S /Q build 17 | @rmdir /S /Q dist 18 | @rmdir /S /Q %PROJ%.egg-info 19 | 20 | :: execute the build... 21 | %PYTHON% setup.py sdist bdist_wheel 22 | 23 | :: check the build... 24 | twine check dist/* 25 | 26 | :: upload to PyPi -- skip for now 27 | twine upload dist/* --verbose 28 | 29 | :: copy/rename the "wheel" to the current dir 30 | @cp dist/%PROJ%-*.tar.gz %PROJ%.tar.gz 31 | 32 | :: cleanup after the build... 33 | @rm -rf build 34 | @rm -rf dist 35 | @rm -rf %PROJ%.egg-info 36 | 37 | @endlocal 38 | -------------------------------------------------------------------------------- /doc/DOC_MAN_SPX.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/doc/DOC_MAN_SPX.pdf -------------------------------------------------------------------------------- /doc/Doxyfile: -------------------------------------------------------------------------------- 1 | @INCLUDE = $(SPEXYGEN)/Spexyfile 2 | 3 | #--------------------------------------------------------------------------- 4 | # Project related configuration options 5 | #--------------------------------------------------------------------------- 6 | PROJECT_NAME = Spexygen 7 | PROJECT_NUMBER = 2.2.5 8 | PROJECT_BRIEF = "Reference Manual" 9 | PROJECT_LOGO = $(SPEXYGEN)/spexygen-awesome/img/logo_spexygen.webp 10 | PROJECT_ICON = 11 | 12 | FULL_PATH_NAMES = NO 13 | EXTENSION_MAPPING = 14 | TYPEDEF_HIDES_STRUCT = YES 15 | EXTRACT_ALL = YES 16 | EXTRACT_PRIVATE = YES 17 | EXTRACT_PRIV_VIRTUAL = YES 18 | EXTRACT_PACKAGE = YES 19 | EXTRACT_STATIC = YES 20 | 21 | #--------------------------------------------------------------------------- 22 | # Build related configuration options 23 | #--------------------------------------------------------------------------- 24 | EXTRACT_LOCAL_CLASSES = YES 25 | EXTRACT_LOCAL_METHODS = YES 26 | EXTRACT_ANON_NSPACES = NO 27 | FORCE_LOCAL_INCLUDES = YES 28 | INLINE_INFO = YES 29 | SORT_MEMBER_DOCS = NO 30 | #--------------------------------------------------------------------------- 31 | # Configuration options related to the input files 32 | #--------------------------------------------------------------------------- 33 | # NOTE: 34 | # In "C with classes", the order matters for correct generation of 35 | # documentation for member functions (e.g., Foo::Foo_ctor()). 36 | # Specifically, the files with documentation (e.g., header.dox) must be 37 | # listed *after* the referenced code (e.g., header.h). 38 | #--------------------------------------------------------------------------- 39 | INPUT = \ 40 | main.dox \ 41 | ../example/main.dox \ 42 | $(SPEXYGEN)/spexygen-awesome/help.dox 43 | 44 | @INCLUDE = spex/Spexyinc 45 | 46 | ENABLED_SECTIONS = IN_SPEXYGEN 47 | OPTIMIZE_OUTPUT_FOR_C = YES 48 | 49 | RECURSIVE = NO 50 | EXCLUDE = 51 | EXCLUDE_SYMLINKS = NO 52 | EXCLUDE_PATTERNS = 53 | EXCLUDE_SYMBOLS = 54 | EXAMPLE_PATH = 55 | EXAMPLE_PATTERNS = * 56 | EXAMPLE_RECURSIVE = NO 57 | IMAGE_PATH = ./img \ 58 | $(SPEXYGEN)/spexygen-awesome/img 59 | #--------------------------------------------------------------------------- 60 | # Configuration options related to source browsing 61 | #--------------------------------------------------------------------------- 62 | SOURCE_BROWSER = YES 63 | INLINE_SOURCES = NO 64 | STRIP_CODE_COMMENTS = NO 65 | #--------------------------------------------------------------------------- 66 | # Configuration options related to the HTML output 67 | #--------------------------------------------------------------------------- 68 | GENERATE_HTML = YES 69 | HTML_OUTPUT = ./html 70 | HTML_FILE_EXTENSION = .html 71 | HTML_HEADER = $(SPEXYGEN)/spexygen-awesome/spexy-header.html 72 | HTML_FOOTER = $(SPEXYGEN)/spexygen-awesome/spexy-footer.html 73 | HTML_STYLESHEET = 74 | #Spexygen HTML_EXTRA_STYLESHEET = 75 | #Spexygen HTML_EXTRA_FILES = 76 | HTML_COLORSTYLE = DARK 77 | HTML_COLORSTYLE_HUE = 209 78 | HTML_COLORSTYLE_SAT = 255 79 | HTML_COLORSTYLE_GAMMA = 113 80 | HTML_DYNAMIC_MENUS = YES 81 | HTML_DYNAMIC_SECTIONS = NO 82 | HTML_CODE_FOLDING = YES 83 | HTML_COPY_CLIPBOARD = YES 84 | HTML_PROJECT_COOKIE = 85 | HTML_INDEX_NUM_ENTRIES = 100 86 | GENERATE_DOCSET = NO 87 | DOCSET_FEEDNAME = 88 | DOCSET_FEEDURL = 89 | DOCSET_BUNDLE_ID = com.state-machine.doc 90 | DOCSET_PUBLISHER_ID = com.state-machine.doc 91 | DOCSET_PUBLISHER_NAME = QuantumLeaps 92 | GENERATE_HTMLHELP = NO 93 | CHM_FILE = 94 | HHC_LOCATION = 95 | GENERATE_CHI = NO 96 | CHM_INDEX_ENCODING = 97 | BINARY_TOC = NO 98 | TOC_EXPAND = NO 99 | SITEMAP_URL = 100 | GENERATE_QHP = NO 101 | QCH_FILE = 102 | QHP_NAMESPACE = com.state-machine.qp 103 | QHP_VIRTUAL_FOLDER = doc 104 | QHP_CUST_FILTER_NAME = 105 | QHP_CUST_FILTER_ATTRS = 106 | QHP_SECT_FILTER_ATTRS = 107 | QHG_LOCATION = 108 | GENERATE_ECLIPSEHELP = NO 109 | ECLIPSE_DOC_ID = com.state-machine.qp 110 | DISABLE_INDEX = NO 111 | GENERATE_TREEVIEW = YES 112 | FULL_SIDEBAR = NO 113 | ENUM_VALUES_PER_LINE = 4 114 | TREEVIEW_WIDTH = 335 115 | EXT_LINKS_IN_WINDOW = NO 116 | OBFUSCATE_EMAILS = NO 117 | HTML_FORMULA_FORMAT = png 118 | FORMULA_FONTSIZE = 10 119 | FORMULA_MACROFILE = 120 | USE_MATHJAX = NO 121 | MATHJAX_VERSION = MathJax_2 122 | MATHJAX_FORMAT = HTML-CSS 123 | MATHJAX_RELPATH = 124 | MATHJAX_EXTENSIONS = 125 | MATHJAX_CODEFILE = 126 | SEARCHENGINE = YES 127 | SERVER_BASED_SEARCH = NO 128 | EXTERNAL_SEARCH = NO 129 | SEARCHENGINE_URL = 130 | SEARCHDATA_FILE = searchdata.xml 131 | EXTERNAL_SEARCH_ID = Spexygen 132 | EXTRA_SEARCH_MAPPINGS = 133 | #--------------------------------------------------------------------------- 134 | # Configuration options related to the LaTeX output 135 | #--------------------------------------------------------------------------- 136 | GENERATE_LATEX = NO 137 | LATEX_OUTPUT = ./latex 138 | LATEX_CMD_NAME = latex 139 | LATEX_HEADER = $(SPEXYGEN)/spexygen-awesome/spexy-header.tex 140 | LATEX_FOOTER = $(SPEXYGEN)/spexygen-awesome/spexy-footer.tex 141 | MAKEINDEX_CMD_NAME = makeindex 142 | LATEX_MAKEINDEX_CMD = makeindex 143 | COMPACT_LATEX = NO 144 | PAPER_TYPE = letter 145 | EXTRA_PACKAGES = 146 | #--------------------------------------------------------------------------- 147 | # Configuration options related to the preprocessor 148 | #--------------------------------------------------------------------------- 149 | ENABLE_PREPROCESSING = YES 150 | MACRO_EXPANSION = NO 151 | EXPAND_ONLY_PREDEF = NO 152 | SEARCH_INCLUDES = YES 153 | INCLUDE_PATH = 154 | INCLUDE_FILE_PATTERNS = 155 | PREDEFINED = 156 | EXPAND_AS_DEFINED = 157 | SKIP_FUNCTION_MACROS = NO 158 | #--------------------------------------------------------------------------- 159 | # Configuration options related to external references 160 | #--------------------------------------------------------------------------- 161 | DOT_PATH = C:/tools/graphviz/bin 162 | -------------------------------------------------------------------------------- /doc/Doxyfile-PDF: -------------------------------------------------------------------------------- 1 | @INCLUDE = Doxyfile 2 | 3 | #--------------------------------------------------------------------------- 4 | # Configuration options related to the LateX output 5 | #--------------------------------------------------------------------------- 6 | PROJECT_NAME = Reference Manual 7 | PROJECT_BRIEF = For Spexygen 2.2.5 8 | PROJECT_NUMBER = Document: DOC_MAN_SPX 9 | 10 | #--------------------------------------------------------------------------- 11 | # Project related configuration options 12 | #--------------------------------------------------------------------------- 13 | GENERATE_HTML = NO 14 | GENERATE_LATEX = YES 15 | 16 | ENABLED_SECTIONS += LATEX 17 | 18 | #--------------------------------------------------------------------------- 19 | # Configuration options related to the input files 20 | # 21 | # NOTE: 22 | # The differet INPUT than in Doxyfile is handled in spex-PDF.json 23 | #--------------------------------------------------------------------------- 24 | 25 | # no source code for this document 26 | SOURCE_BROWSER = NO 27 | VERBATIM_HEADERS = NO 28 | -------------------------------------------------------------------------------- /doc/img/spexygen-video.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/doc/img/spexygen-video.jpg -------------------------------------------------------------------------------- /doc/img/spx-code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/doc/img/spx-code.jpg -------------------------------------------------------------------------------- /doc/img/spx-uid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/doc/img/spx-uid.jpg -------------------------------------------------------------------------------- /doc/img/spx-uid.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/doc/img/spx-uid.webp -------------------------------------------------------------------------------- /doc/img/spx.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/doc/img/spx.odg -------------------------------------------------------------------------------- /doc/main.dox: -------------------------------------------------------------------------------- 1 | /*! @mainpage Spexygen 2 | @anchor main 3 | 4 | @image html banner_spexygen.webp 5 | @image latex banner_spexygen.jpg width=6.5in 6 | 7 | @ifnot LATEX 8 | @nav_next{work} 9 |
10 | @endif 11 | 12 | @remark 13 | - This _Spexygen_ documentation has been created with _Spexygen_. 14 | - _Spexygen_ is available on @webref{https://github.com/QuantumLeaps/spexygen,GitHub under the permissive MIT open-source license}. 15 | @if LATEX 16 | ![](logo_spexygen.png) 17 | @else 18 | [![](logo_spexygen.png)](https://github.com/QuantumLeaps/spexygen) 19 | @endif 20 | 21 | @section spx_about About Spexygen 22 | @webref{https://github.com/QuantumLeaps/spexygen,Spexygen} is a @webref{https://Doxygen.nl,Doxygen} extension for creating @ref tr "traceable" technical specifications, such as: 23 | - traceable @ref srs "requirement specifications" 24 | - traceable @ref header.h "source code" 25 | - traceable @ref test1.c "tests" 26 | - traceable specifications of other kind 27 | 28 | @ifnot LATEX 29 | 30 | 31 | 32 | @else 33 | @image latex spexygen-video.jpg width=5in 34 | @webref{https://youtu.be/5naC_z-76Aw?si=x4Svroqv9qDdkpsE,Spexygen video} 35 | @endif 36 |
37 | 38 | @note 39 | By extending Doxygen with a uniform __traceability management__ not just for source code, but also for all other specifications, _Spexygen_ supports regulatory compliance with functional safety standards such as IEC 61508, IEC 62304, ISO 26262 and others. 40 | 41 | @subsection spx_feat Spexygen Features 42 | The main objectives and features of _Spexygen_ are: 43 | - uniform management of @ref tr "traceability" within all documents in the system, including source code 44 | - provision of commands for creating well-structured, uniformely formatted, fully @ref tr "traceable" "work artifacts" 45 | - automating the generation of @ref tr-fw "forward traceability" links in the documentation (_Spexygen_ generates @ref tr-fw-rec "recursive forward traceability" enabling impact analysis to identify the potential consequences of a change of a given artifact) 46 | - automating the generation of brief descriptions for the @ref tr-bw "backward traceability" links 47 | - enabling DRY documentation (designed according to the "Don't Repeat Yourself" principle) by eliminating repetitions in specifying dependencies among "work artifacts" 48 | - genearting cross-linked, __searchable__, nicely formatted documentation in HTML (modern @webref{https://github.com/jothepro/Doxygen-awesome-css, Doxygen-awesome HTML style}) 49 | - genearting cross-linked, nicely formatted documentation in PDF (modern LaTeX template) 50 | - representing documentation in human-readable text files, which can be stored in any version control system (VCS). 51 | 52 | 53 | @section tr Traceability 54 | __Traceability__ is the cornerstone of any formal documentation system, especially those intended for managing __functional safety__. It enables product teams to associate every work artifact (e.g., a specific requirement) with all the related project artifacts (e.g., design, code, or tests), both backward and forward. Traceability lets everyone to see how every work artifact relates to the requirement — and vice versa — at any point during development. This ability fosters team collaboration and enables early detection of possible production risks. 55 | 56 | @attention 57 | _Spexygen_ provides consistent and __automated__ management of traceability within the whole documentation set. 58 | 59 | @subsection tr-uid Unique Identifiers (UIDs) 60 | Traceability is enabled by the consistent use of __Unique Identifiers (UIDs)__, which are short text labels associated with _all_ work artifacts, such as requirements, architecture elements, design elements, coding standard deviations, tests, functional safety documents, etc. 61 | 62 | @note 63 | The structure of UIDs is __flexible__ to accommodate various existing naming conventions. But for compatibility with the widest possible range of cross-referencing systems and tools, the UIDs are __restricted__ to generally follow the _rules for identifiers in programming languages_, such as identifiers in C, C++, or Python. Specifically, valid UIDs can contain only upper-case letters (A..Z), numbers (0..9), and underscores ('_'). Among others, UIDs cannot contain spaces, punctuation, parentheses, or any special characters like !, \@, #, \$, etc. 64 | 65 | @remark 66 | Restricting the UIDs to the programming language identifiers allows you to _use the UIDs as identifiers_. For example, you might name test functions as their UIDs. Additionally, such UIDs become __searchable__ with the Doxygen built-in search (the "Search Box"). 67 | 68 | 69 | The most important feature of UIDs is their __uniqueness__ within the whole system under consideration. To avoid name conflicts, it is advantageous to establish general rules for constructing the UIDs. Throughout _Spexygen_ documentation, the UIDs have the general structure consisting of fields separated by underscores: 70 | 71 | @verbatim 72 | +------------------ [1] Work artifact class (e.g., 'SRS' for Software Requirement Specification) 73 | | +-------------- [2] Component unique identifier (e.g, 'PRJ' for my "Project") 74 | | | +---------- [3] Work artifact ID (abbreviation or number) 75 | | | | +------- [4] Work artifact number 76 | | | | | +--- [5] Optional variant letter ('A', 'B', 'C'...) 77 | | | | | |+-- [6] Optional version number (1, 2, 3...) 78 | | | | | || 79 | SRS_xxx_yyy_zz[_A2] 80 | @endverbatim 81 | Examples: @tr{SRS_PRJ_Foo_01}, @tr{TUN_PRJ_free_fun_00} 82 | 83 | The various fields in the UID are as follows: 84 | 85 | `[1]` the UID starts with a fields corresponding to the _class_ of the work artifact. Here are the suggested artifact class names: 86 | + DOC Document 87 | + SRS Software Requirement Specification 88 | + SSR Software Safety Requirement 89 | + SAS Software Architecture Specification 90 | + SDS Software Design Specification 91 | + FSM Functional Safety Management artifact 92 | + SHR Software Hazard and Risk artifact 93 | + DVR Deviation Record (e.g. coding standard violation) 94 | + DVP Deviation Permit (e.g. coding standard violation) 95 | + TUN Test (unit) 96 | + TIN Test (integration) 97 | + TAC Test (acceptance) 98 | 99 | `[2]` the Component Unique Identifier (CUI), which should be unique enough to avoid name conflicts with other software components in a larger system. 100 | 101 | `[3]` "Work artifact ID" field identifies the artifact within the "work artifact class" `[1]`. This is the most flexible part in the UID to accommodate other existing conventions, such as MISRA deviations, the `work artifact ID` field should be easily identifiable with the MISRA Rule/Directive ID, such as `D04_01` for "Directive 4.1", or `R10_04` for "Rule 10.4". Still, please note that the more structured UID convention of using two-digits for feature groups (e.g., `D04_10` instead of `D4_10`) provide additional benefits, such as correct order under a simple alphabetical sort. This property is missing in the original MISRA IDs (e.g., a simple alphabetical sort will place Rule 10.8 _before_ Rule 8.10). 102 | 103 | `[4]` "Work artifact number" filed identifies the aspect of the work artifact 104 | 105 | `[5]` optionally, the UID might contain a variant letter ('A', 'B', 'C',...) 106 | 107 | `[6]` optionally, the UID might end with a single-digit version number (0..9). 108 | 109 | Alternatively, UIDs of __code__ elements follow the rules established by Doxygen, with the following general form: 110 | @verbatim 111 | +-------------- [1] Namespace (e.g., class or module) 112 | | +--- [2] element (e.g., attribute or operation) 113 | | | 114 | [namespace]_[element] 115 | @endverbatim 116 | Examples: @tr{Foo_ctor()}, @tr{TUN_PRJ_Foo_ctor_01} 117 | 118 | @subsection tr-bw Backward Traceability 119 | Backward traceability begins at a specific work artifact and links it to the original artifact. For example, architecture element can be linked with an upstream requirement, or code artifact with the upstream design. Backward traceability gives visibility into why specific artifacts were created and how different pieces of a system fit together. Tracing in this way also allows testers to find gaps or missing work artifacts. 120 | 121 | @remark 122 | Backward traceability is the most natural and efficient way of specifying hierarchical relationships, such as superclass-subclass in object-oriented programming (OOP). Class inheritance is universally represented in the subclasses, which store their superclass (backward traceability). In contrast, superclasses don't show their subclasses (@ref tr-fw "forward traceability"). The _Spexygen_ documentation applies this approach to all work artifacts, starting with the requirements at the top, through architecture, design, __source code__, tests, deviations, etc. 123 | 124 | As illustrated in the diagram below, __backward traceability is provided explicitly__ in the _Spexygen_ documentation and the source code. Specifically, the downstream work artifacts provide trace information to the related upstream artifact by means of the @ref tr-uid "Unique Identifier (UIDs)". 125 | 126 | @image html spx_tr.svg width=1500px 127 | @image latex spx_tr.eps 128 | @caption{Schematic View of Backward Traceability in the _Spexygen_ documentation} 129 | 130 | @note 131 | The _Spexygen_ documentation traceability system includes the __source code__. This is achieved by placing special backward traceability links, such as `@tr{SRS_PRJ_Foo_03}` or `@tr{free_fun()}`, inside the _Spexygen_ documentation for the source code. 132 | 133 | 134 | @subsection tr-fw Forward Traceability 135 | Forward traceability begins at the original artifact and links it with all the resulting forward work items. For example, a requirement can be linked with source code elements that implement that requirement. This type of trace ensures that each original artifact (e.g., requirement) is not only satisfied but verified and validated. In the _Spexygen_ documentation the forward traceability is __generated automatically__ by the `spexygen.py` Python script. 136 | 137 | @anchor tr-fw-rec 138 | @note 139 | Forward traceability is typically __recursive__ meaning that if artifact A traces to B and B traces to C, then artifact A also traces to C. _Spexygen_ generates __recursive forward trace__, which enables the teams to perform _impact analysis_ to identify the potential consequences of a change of a given artifact. 140 | 141 | 142 | @subsection tr-bi Bidirectional Traceability 143 | Bidirectional traceability is the ability to perform both forward and backward traceability. Bidirectional traceability is the optimal type of traceability because it gives teams full visibility from requirements through architecture, design, source code, tests, and back again. The system implemented in the _Spexygen_ documentation provides such bidirectional traceability. 144 | 145 | @remark 146 | The whole system of traceability offered in _Spexygen_ is __extensible__ and can be used in any technical documentation system. 147 | 148 | @ifnot LATEX 149 |
150 | @nav_next{work} 151 | @endif 152 | */ 153 | //############################################################################ 154 | /*! @page work Working with Spexygen 155 | 156 | @ifnot LATEX 157 | @nav{tr,doc} 158 |
159 | @endif 160 | 161 | @section spx_work Spexygen Workflow 162 | _Spexygen_ can be viewed as a pre-processor for @webref{https://Doxygen.nl,Doxygen} with the workflow similar to that of Doxygen. The diagram below shows the documentation generation steps, the 163 | relation between _Spexygen_ and Doxygen, and the flow of information between them. The numbered sections following the diagram explain the labeled elements: 164 | 165 |
166 | @image html spx_flow.svg width=1500px 167 | @image latex spx_flow.eps 168 | @caption{Spexygen information flow.} 169 | 170 | `[1]` Developers prepare documents (e.g., `srs.dox`) according to the conventions established by Doxygen. The individual "work artifacts" are created with a set of @ref spx_use-doc-uid "custom commands" provided by _Spexygen_ (file `Spexyfile`). For example, command \c \@uid{} starts a definition of a new "work artifact" such as a requirement. 171 | 172 | `[2]` Developers also apply the special @ref spx_use-doc-code "custom commands" provided by _Spexygen_ to annotate the source code (e.g., `header.h`). For example, command \c \@code_uid{} starts a definition of a new "code artifact". 173 | 174 | `[3]` Developers prepare a _Spexygen_ configuration file (e.g., `spex.json` in the diagram), which describes the documents to be traced and generated by _Spexygen_. 175 | 176 | `[4]` The `spexygen.py` Python script processes the files and generates forward-traceabilty in requested locations in the provided files. 177 | 178 | `[5]` The `spexygen.py` Python script creates a specified directory (`spex` in the diagram, see also `"gen-dir":` tag in `spexy.json`) to save the generated files. 179 | 180 | `[6-7]` The `spexygen.py` Python script generates the augmented files into a specified directory. The generated files contain all the original information plus the tracing information generated by _Spexygen_ (shown in red in the diagram) 181 | 182 | `[8]` The `spexygen.py` Python script also generates the `Spexyinc` file that contains the information about all generated files. 183 | 184 | `[9]` Developers can prepare other documentation, not processed by _Spexygen_, but also included in the final Doxygen-generated documentation. 185 | 186 | `[10]` The Doxygen configuration file (`Doxyfile`) includes the `Spexyfile` (with the _Spexygen_ custom commands) and the generated `Spexyinc` file (with files generated by _Spexygen_ and to be processed by Doxygen) 187 | 188 | `[11]` Doxygen processes the files generarted by _Spexygen_ (see `[6-7]`) and other files (see `[9]`) according to the `Doxyfile` (see `[10]`) 189 | 190 | `[12]` If Doxygen is configured to produce HTML output, it applies the modern @webref{https://jothepro.github.io/Doxygen-awesome-css/,Doxygen-awesome HTML styling} (included in _Spexygen_) 191 | 192 | `[13]` If Doxygen is configured to produce PDF output, it applies the modern LaTeX template provided by _Spexygen_ 193 | 194 | @note 195 | The _Spexygen_ workflow is illustrated in the provided @ref exa "example". 196 | 197 | @subpage doc " " 198 | @subpage gen " " 199 | 200 | @ifnot LATEX 201 |
202 | @nav{tr,doc} 203 | @endif 204 | */ 205 | //############################################################################ 206 | /*! @page doc Preparing Spexygen Documentation 207 | 208 | @ifnot LATEX 209 | @nav{work,gen} 210 |
211 | @endif 212 | 213 | As described in the @ref spx_work "spexygen workflow", _Spexygen_ works with the documentation and annotated code prepared according to the Doxygen conventions. However, _Spexygen_ provides a layer of customized commands for defining @ref tr "traceable" "work artifacts", which can be of two kinds: 214 | 1. @ref spx_use-doc-uid "work artifacts" created in pure documentation, such as requirements specification; and 215 | 2. @ref spx_use-doc-code "code artifacts" created by Doxygen for various elements of source code, such as classes, functions, macros, etc. 216 | 217 | @section spx_use-doc-uid Defining Work Artifacts 218 | __Work artifacts__ (e.g., requirements) are defined by means of the following _Spexygen_ custom commands: 219 | 220 |
Command Purpose 221 |
\@uid{uid,brief} starts the definition of a "work artifact"
parameter: uid -- the @ref tr-uid "UID" of the "work artifact"
parameter: brief -- brief description of the "work artifact" 222 |
\@uid_litem{title} adds new line - item in the "work artifact" definition
parameter: title -- Title of the line item(e.g., Details) 223 |
\@uid_bw_trace{brief} adds the backward trace section in in the "work artifact" definition
parameter: brief -- request _Spexygen_ to add the brief item description 224 |
\@uid_bw_trace adds the backward trace section in in the "work artifact" definition
overloaded version without requesting the brief description 225 |
\@uid_fw_trace{levels} adds the forward trace section in in the "work artifact" definition
this is a request to _Spexygen_ to __generate__ the recursive @ref tr-fw "forward traceability" for the "work artifact"
optional parameter: levels truncates the displayed recursion levels 226 |
\@enduid ends the definition "work artifact"
must be placed at the end of "work artifact" definition 227 |
\@tr{uid} references the given UID
parameter: uid -- the @ref tr-uid "UID" of the "work artifact" 228 |
229 | @remark 230 | The _Spexygen_ custom commands are defined as `ALIAS=...` in `Spexyfile`. 231 | 232 | The following snippet illustrates how a "work artifact" (a requirement) has been documented for _Spexygen_ (see also file srs.dox): 233 | @verbatim 234 | [1] @section srs_req Requirements 235 | ... 236 | [2] @uid{SRS_PRJ_Foo_03,My project class Foo shall provide a verify operation.} 237 | [3] @uid_litem{Description} 238 | Longer description of the requirement 239 | [4] @uid_bw_trace{brief} 240 | [5] - @tr{SRS_PRJ_Foo_00} 241 | [6] @uid_fw_trace{2} 242 | [7] @enduid 243 | @endverbatim 244 | 245 | `[1]` _Spexygen_ "work artifacts" must be defined in the scope of a Doxygen \@section. 246 | 247 | `[2]` each "work artifact" is defined with the _Spexygen_ command \@uid{}, which takes two arguments: 248 | 1. the UID associated with the artifact (e.g., `SRS_PRJ_Foo_03`) 249 | 2. the brief description of the artifact (e.g., `My project class Foo shall provide a verify operation.`) 250 | @attention 251 | The whole \@uid{} command must be defined in a single line of text and the brief description must not contain commas `,` 252 | 253 |

254 | `[3]` the "work artifact" definition can contain "line items", such as "Description" coded by means of the \@uid_litem{Description} _Spexygen_ command. A "work artifact" can have multiple line items defined with the \@uid_litem{} _Spexygen_ command. 255 | 256 | `[4]` the "work artifact" can specify @ref tr-bw "backward traceability" by means of the \@uid_bw_trace _Spexygen_ command. If this command provides argument `{brief}`, _Spexygen_ will generate the brief description for each of the provided traceability links (see the next step `[5]`) 257 | 258 | `[5]` the traceability links to the upstream artifacts must be provided explicitly by means of the \@tr{uid} _Spexygen_ command. The command establishes traceability to the @ref tr-uid "UID" provided in the argument (e.g., `SRS_PRJ_Foo_00`) 259 | 260 | `[6]` the "work artifact" can specify forward traceability by means of the \@uid_fw_trace _Spexygen_ command 261 | @note 262 | The \@uid_fw_trace _Spexygen_ command is just a __placeholder__ for _Spexygen_ to generate the forward traceability links at this place. The optional parameter truncates the number of recursion levels to the specified value. 263 | 264 | `[7]` The artifact definition must end with the _Spexygen_ \@enduid command 265 | 266 | The following screen shot shows how the "work artifact" defined in the code snipped above is rendered in HTML: 267 | 268 | @image html spx-uid.jpg 269 | @image latex spx-uid.jpg width=5.5in 270 | @caption{Examples of "work artifacts" (requirements)} 271 | 272 | @section spx_use-doc-code Defining Code Artifacts 273 | __Code artifacts__ (e.g., functions, macros, classes) are naturally handled by Doxygen and the _Spexygen_ system must comply with the exiting Doxygen conventions. The "code artifacts" are defined by means of the following _Spexygen_ custom commands: 274 | 275 |
Command Purpose 276 |
\@code_uid{uid,brief } starts the definition of a "code artifact"
parameter: uid --the @ref tr-uid "UID" of the "code artifact"
parameter: brief -- brief description of the "code artifact" 277 |
\@code_litem{item } adds new line-item in the "code artifact" definition
parameter: title --Title of the line item(e.g., Details) 278 |
\@code_bw_trace{brief} adds the backward trace section in in the "code artifact" definition
parameter: brief -- request _Spexygen_ to add the brief item description 279 |
\@code_bw_trace adds the backward trace section in in the "code artifact" definition
overloaded version without requesting the brief description 280 |
\@code_fw_trace{levels} adds the forward trace section in in the "code artifact" definition
this is a request to _Spexygen_ to __generate__ the recursive @ref tr-fw "forward traceability" for the "code artifact"
optional parameter: levels truncates the displayed recursion levels 281 |
\@endcode_uid ends the definition "code artifact"
must be placed at the end of "code artifact" definition 282 |
\@tr{uid} references the given UID
parameter: uid -- the @ref tr-uid "UID" of the "code artifact" 283 |
284 | @remark 285 | The _Spexygen_ custom commands are defined as `ALIAS=...` in `Spexyfile`. 286 | 287 | The following snippet illustrates how a "code artifact" (function `free_fun()`) has been documented (see also file header.h): 288 | @verbatim 289 | /*! 290 | [1] @code_uid{Foo_verify_(), Operation verify_() of class Foo brief description} 291 | [2] @code_litem{Details} 292 | Operation verify_() of class Foo longer description. 293 | 294 | @param[in] me - the instance pointer (OOP in C) 295 | 296 | [3] @code_bw_trace{brief} 297 | [4] - @tr{SRS_PRJ_Foo_03} 298 | [5] @code_fw_trace{3} 299 | [6] @endcode_uid 300 | */ 301 | [7] bool Foo_verify_(Foo const* const me); 302 | @endverbatim 303 | 304 | `[1]` each "code artifact" is defined with the _Spexygen_ command \@code_uid{}, which takes two arguments : 305 | 1. the UID associated with the artifact(e.g., `Foo_verify_()`) 306 | 2. the brief description of the artifact(e.g., `Operation verify_() of class Foo brief description`) 307 | @attention 308 | The whole \@code_uid{} command must be defined in a single line of text and the brief description must not contain commas `,` 309 | 310 |

311 | `[2]` the "code artifact" definition can contain "line items", such as "Details" coded by means of the \@code_litem{Details} _Spexygen_ command. A "code artifact" can have multiple items defined with the \@code_litem{...} _Spexygen_ command. 312 | 313 | `[3]` the "code artifact" can specify @ref tr-bw "backward traceability" by means of the \@uid_bw_trace _Spexygen_ command. If this command provides argument `{brief}`, _Spexygen_ will generate the brief description for each of the provided traceability links(see the next step `[4]`) 314 | 315 | `[4]` the traceability links to the upstream artifacts must be provided explicitly by means of the \@tr{uid} _Spexygen_ command. The command establishes traceability to the @ref tr-uid "UID" provided in the argument(e.g., `SRS_PRJ_Foo_03`) 316 | 317 | `[5]` the "code artifact" can specify forward traceability by means of the \@code_fw_trace _Spexygen_ command 318 | @note 319 | The \@code_fw_trace _Spexygen_ command is just a __placeholder__ for _Spexygen_ to generate the forward traceability links at this place. The optional parameter truncates the number of recursion levels to the specified value. 320 | 321 | `[6]` The "code artifact" definition must end with the _Spexygen_ \@endcode_uid command 322 | 323 | `[7]` Finally, the "code artifact" needs to be declared, so that Doxygen can analyze the syntax of the specific programming language. 324 | 325 | 326 | The following screen shot shows how the "code artifact" defined in the code snipped above is rendered in HTML : 327 | 328 | @image html spx-code.jpg 329 | @image latex spx-code.jpg width=5.5in 330 | @caption{ Examples of "code artifacts" (function) } 331 | 332 | 333 | @ifnot LATEX 334 |
335 | @nav{work,gen} 336 | @endif 337 | */ 338 | //############################################################################ 339 | /*! @page gen Generating Spexygen Documentation 340 | 341 | @ifnot LATEX 342 | @nav{doc,exa} 343 |
344 | @endif 345 | 346 | As shown in the @ref work "spexygen workflow diagram", _Spexygen_ documentation generation is handled by the Python script `spexygen.py`. This script processes the input files in two passes. In the first pass (called "trace"), the `spexygen.py` script parses the files for the special _Spexygen_ commands and collects the information about the dependencies among the various artifacts (based on their @ref tr-uid "UIDs"). In the second pass (called "gen"), the `spexygen.py` script generates the output-files by replacing the special forward traceability commands (\@ui_fw_trace and \@code_fw_trace) with the actual traceability links collected during the first pass. 347 | 348 | @section gen_spex Spexygen Configuration File 349 | Similar to Doxygen, _Spexygen_ is configured by an external configuration file with the default name `spex.json`. That file specifies about the input files and output directory and files. The file is structure according to the @webref{https://www.json.org/,JSON format}. Below is an annotated example of `spex.json` file: 350 | 351 | @verbatim 352 | { 353 | [1] "trace": [ 354 | "../example/exa.dox", 355 | "../example/srs.dox", 356 | "../example/dev.dox", 357 | "../example/inc", 358 | "../example/src", 359 | "../example/test" 360 | ] 361 | 362 | [2] "gen-dir": "spex", 363 | [3] "gen-inc": "Spexyinc", 364 | [4] "gen": [ 365 | "../example/exa.dox", 366 | "../example/srs.dox", 367 | "../example/dev.dox", 368 | "../example/inc", 369 | "../example/src", 370 | "../example/test" 371 | ], 372 | } 373 | @endverbatim 374 | 375 | `[1]` The `"trace":` JSON array contains a list of the input files that _Spexygen_ will "trace". 376 | @remark 377 | The `"trace":` array is optional and if it is not provided, _Spexygen_ will "trace" the files specified in the `"gen":` array. 378 | 379 |

380 | `[2]` The `"gen-dir":` JSON string specifies the output-directory into which _Spexygen_ will generate the output 381 | @remark 382 | The "gen-dir":` JSON string is optional and if not provided, _Spexygen_ will use the default directory `spex` 383 | 384 |

385 | `[3]` The "gen-inc": JSON string specifies the spexygen include-file for Doxygen, which contains the list of the generated files. It is intended for inclusion in the `Doxyfile`. The spexygen include-file is generated in the output-directory. 386 | @remark 387 | The "gen-inc":` JSON string is optional and if not provided, _Spexygen_ will NOT generate the include-file. 388 | 389 |

390 | `[4]` The `"gen":` JSON array contains a list of the files that _Spexygen_ will generate by replacing the special forward traceability commands (\@ui_fw_trace and \@code_fw_trace) with the actual traceability links collected during the "trace" pass. 391 | 392 | 393 | @section gen_run Running Spexygen 394 | The `spexygen.py` Python script can be executed from a command prompt. Typically, you run the script in the directory, where you have your `spex.json` script.Here is an example run: 395 | 396 | @verbatim 397 | cd spexygen/doc <-- the directory with the spex.json config file 398 | python ../spexygen.py 399 | 400 | Spexygen: traceable technical documentation system 2.0.0 401 | Copyright (c) 2005-2024 Quantum Leaps, www.state-machine.com 402 | 403 | Tracing: ../example/exa.dox 404 | Tracing: ../example/srs.dox 405 | Tracing: ../example/dev.dox 406 | Tracing: ../example/inc/header.h 407 | Tracing: ../example/src/source.c 408 | Tracing: ../example/test/test1.c 409 | Tracing: ../example/test/test2.c 410 | Generating: spex/exa.dox 411 | Generating: spex/srs.dox 412 | Generating: spex/dev.dox 413 | Generating: spex/header.h 414 | No forward trace for UID: "Foo" 415 | No forward trace for UID: "Foo_inst" 416 | Generating: spex/source.c 417 | Generating: spex/test1.c 418 | Generating: spex/test2.c 419 | @endverbatim 420 | 421 | @note 422 | The `spexygen.py` script takes one command-line parameter, which is the name of the configuration file. If not provided, the default name is `spex.json`. 423 | 424 | @remark 425 | The `spexygen.py` Python script is also available in the @webref{https://pypi.org/project/spexygen,PyPi package manager} and can be installed with the standard Python package installer `pip`: 426 | @verbatim 427 | pip install spexygen 428 | @endverbatim 429 | After such installation, you run _Spexygen_ simply as follows: 430 | @verbatim 431 | spexygen 432 | @endverbatim 433 | 434 | Either way, depending on your settings _Spexygen_ will create the output-directory (e.g., `spex`) with all the generated files. 435 | 436 | @subsection gen_inc Spexygen Include File 437 | _Spexygen_ can generate the spexygen include-file for Doxygen, which contains the list of the generated files. It is intended for inclusion in the `Doxyfile`. The spexygen include-file is generated in the output-directory. Here is an example: 438 | 439 | @verbatim 440 | INPUT += \ 441 | spex/exa.dox \ 442 | spex/srs.dox \ 443 | spex/dev.dox \ 444 | spex/header.h \ 445 | spex/source.c \ 446 | spex/test1.c \ 447 | spex/test2.c 448 | @endverbatim 449 | 450 | 451 | @section gen_doxy Doxygen Configuration File 452 | The _Spexygen_ output (plus other files) can be now fed to Doxygen. However, before you can run Doxygen, you need the @webref{https://www.Doxygen.nl/manual/config.html,Doxygen configuration file}. That configuration file must include the @ref spx_use-doc-uid "spexygen special commands" and the files generated by _Spexygen_. Here is an example of an annotated `Doxyfile` with these elements: 453 | 454 | @verbatim 455 | [1] @INCLUDE = $(SPEXYGEN)/Spexyfile 456 | [2] INPUT = main.dox 457 | [3] @INCLUDE = spex/Spexyinc 458 | [4] INPUT += ... 459 | @endverbatim 460 | 461 | `[1]` The Doxygen `\@INCLUDE` tag includes the `Spexyfile` located in the `$(SPEXYGEN)` directory defined here by means of an environment variable. 462 | @attention 463 | The `SPEXYGEN` environment variable must be defined in your system to point to the _spexgyen_ installation directory. 464 | 465 |

466 | `[2]` The @webref{https://www.Doxygen.nl/manual/config.html#cfg_input,`INPUT` tag} specifies the input files for Doxygen. Here you can specify files that you wish to include in the Doxygen output, but which have not been processed by _Spexygen_. 467 | 468 | `[3]` This Doxygen `\@INCLUDE = spex/Spexyinc` tag includes the @ref gen_inc "spexgyen-include file" with the Doxygen input generated by _Spexygen_. 469 | 470 | `[4]` Any additional Doxygen `INPUT` (not produced by _spexyen_) can be specified as well (please note the `+=` operator as opposed to `=`) 471 | 472 | 473 | @section gen_doxy-run Running Doxygen 474 | Once the `$(SPEXYGEN)` environment variable has been defined, Doxygen can be run as usual from the directolry with the `Doxyfile`: 475 | 476 | @verbatim 477 | Doxygen 478 | @endverbatim 479 | 480 | 481 | @subsection gen_all Combining Spexygen & Doxygen 482 | In practice, most convenient is combining _Spexygen_ and Doxygen and run both automatically one after another. Here is an example Windows batch file that automates the process (see `spexygen/doc/make.bat`): 483 | 484 | @note 485 | The provided `make.bat` can generate @webref{https://www.state-machine.com/spexygen,HTML} and @webref{https://www.state-machine.com/spexygen/DOC-MAN-SPX.pdf,PDF} output formats. 486 | 487 | @verbatim 488 | @setlocal 489 | 490 | @echo usage: 491 | @echo make 492 | @echo make -PDF 493 | 494 | :: tools (adjust to your system)---------------------------------------------- 495 | :: Doxygen/Spexygen tools 496 | @set DOXYGEN=Doxygen 497 | @set SPEXYGEN=.. 498 | 499 | @echo Generate Spexygen tracing ---------------------------------------------- 500 | rmdir /S /Q .\spex 501 | python %SPEXYGEN%/spexygen.py spex.json 502 | 503 | ::============================================================================ 504 | @if "%1"=="-PDF" goto PDF 505 | 506 | @echo Generate HTML Documentation -------------------------------------------- 507 | @set HTML_OUT=html 508 | 509 | @echo. 510 | @echo cleanup 511 | rmdir /S /Q %HTML_OUT% 512 | 513 | @echo generating HTML... 514 | %DOXYGEN% Doxyfile 515 | 516 | @echo Adding custom files... 517 | copy %SPEXYGEN%\spexygen-awesome\jquery.js %HTML_OUT% 518 | 519 | ::qclean %HTML_OUT% 520 | goto END 521 | 522 | :PDF 523 | @echo Generate PDF Documentation -------------------------------------------- 524 | @set LATEX_OUT=latex 525 | 526 | @echo. 527 | @echo cleanup 528 | rmdir /S /Q %LATEX_OUT% 529 | 530 | @echo generating LATEX... 531 | %DOXYGEN% Doxyfile-PDF 532 | 533 | :: Generate LaTex/PDF Documentation... 534 | @echo generating PDF... 535 | @cd %LATEX_OUT% 536 | @call make.bat 537 | @copy refman.pdf ..\DOC-MAN-SPX.pdf 538 | @cd .. 539 | rmdir /S /Q %LATEX_OUT% 540 | 541 | :END 542 | @echo Final cleanup ---------------------------------------------------------- 543 | rmdir /S /Q .\spex 544 | 545 | @endlocal 546 | @endverbatim 547 | 548 | 549 | @ifnot LATEX 550 |
551 | @nav{doc,exa} 552 | @endif 553 | */ 554 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: =========================================================================== 3 | :: Product: batch script for generating Spexygen documentation 4 | :: 5 | :: Q u a n t u m L e a P s 6 | :: ------------------------ 7 | :: Modern Embedded Software 8 | :: 9 | :: Copyright (C) 2005 Quantum Leaps, LLC . 10 | :: 11 | :: SPDX-License-Identifier: MIT 12 | :: 13 | :: Permission is hereby granted, free of charge, to any person obtaining a copy of 14 | :: this software and associated documentation files (the "Software"), to deal in 15 | :: the Software without restriction, including without limitation the rights to 16 | :: use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 17 | :: the Software, and to permit persons to whom the Software is furnished to do so, 18 | :: subject to the following conditions: 19 | :: 20 | :: The above copyright notice and this permission notice shall be included in all 21 | :: copies or substantial portions of the Software. 22 | :: 23 | :: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | :: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 25 | :: FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 26 | :: COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 27 | :: IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 28 | :: CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | :: 30 | :: 31 | :: 32 | :: =========================================================================== 33 | @setlocal 34 | 35 | @echo usage: 36 | @echo make 37 | @echo make -PDF 38 | 39 | :: tools (adjust to your system)---------------------------------------------- 40 | :: Doxygen/Spexygen tools 41 | @set DOXYGEN=doxygen 42 | @set SPEXYGEN=.. 43 | 44 | @echo Generate Spexygen tracing ---------------------------------------------- 45 | rmdir /S /Q .\spex 46 | python %SPEXYGEN%/spexygen.py spex.json 47 | 48 | ::============================================================================ 49 | @if "%1"=="-PDF" goto PDF 50 | 51 | @echo Generate HTML Documentation -------------------------------------------- 52 | @set HTML_OUT=html 53 | 54 | @echo. 55 | @echo cleanup 56 | rmdir /S /Q %HTML_OUT% 57 | 58 | @echo generating HTML... 59 | %DOXYGEN% Doxyfile 60 | 61 | @echo Adding custom files... 62 | copy %SPEXYGEN%\spexygen-awesome\jquery.js %HTML_OUT% 63 | @copy DOC_MAN_SPX.pdf %HTML_OUT% 64 | 65 | ::qclean %HTML_OUT% 66 | goto END 67 | 68 | :PDF 69 | @echo Generate PDF Documentation -------------------------------------------- 70 | @set LATEX_OUT=latex 71 | 72 | @echo. 73 | @echo cleanup 74 | rmdir /S /Q %LATEX_OUT% 75 | 76 | @echo generating LATEX... 77 | %DOXYGEN% Doxyfile-PDF 78 | 79 | :: Generate LaTex/PDF Documentation... 80 | @echo generating PDF... 81 | @cd %LATEX_OUT% 82 | @call make.bat 83 | @copy refman.pdf ..\DOC_MAN_SPX.pdf 84 | @cd .. 85 | rmdir /S /Q %LATEX_OUT% 86 | 87 | :END 88 | @echo Final cleanup ---------------------------------------------------------- 89 | rmdir /S /Q .\spex 90 | 91 | @endlocal 92 | -------------------------------------------------------------------------------- /doc/spex.json: -------------------------------------------------------------------------------- 1 | { 2 | "gen-dir": "spex", 3 | "gen-inc": "Spexyinc", 4 | "gen": [ 5 | "../example/srs.dox", 6 | "../example/inc", 7 | "../example/src", 8 | "../example/test" 9 | ], 10 | "note1": "no separate 'trace' section, meaning that 'gen' section will be used for tracing" 11 | } -------------------------------------------------------------------------------- /example/Doxyfile: -------------------------------------------------------------------------------- 1 | @INCLUDE = $(SPEXYGEN)/Spexyfile 2 | 3 | #--------------------------------------------------------------------------- 4 | # Project related configuration options 5 | #--------------------------------------------------------------------------- 6 | PROJECT_NAME = Example 7 | PROJECT_NUMBER = 0.0.1 8 | PROJECT_BRIEF = For Spexygen 2.2.5 9 | PROJECT_LOGO = $(SPEXYGEN)/spexygen-awesome/img/logo_spexygen.webp 10 | PROJECT_ICON = 11 | 12 | FULL_PATH_NAMES = NO 13 | EXTENSION_MAPPING = 14 | TYPEDEF_HIDES_STRUCT = YES 15 | EXTRACT_ALL = YES 16 | EXTRACT_PRIVATE = YES 17 | EXTRACT_PRIV_VIRTUAL = YES 18 | EXTRACT_PACKAGE = YES 19 | EXTRACT_STATIC = YES 20 | 21 | #--------------------------------------------------------------------------- 22 | # Build related configuration options 23 | #--------------------------------------------------------------------------- 24 | EXTRACT_LOCAL_CLASSES = YES 25 | EXTRACT_LOCAL_METHODS = YES 26 | EXTRACT_ANON_NSPACES = NO 27 | FORCE_LOCAL_INCLUDES = YES 28 | INLINE_INFO = YES 29 | SORT_MEMBER_DOCS = NO 30 | #--------------------------------------------------------------------------- 31 | # Configuration options related to the input files 32 | #--------------------------------------------------------------------------- 33 | # NOTE: 34 | # In "C with classes", the order matters for correct generation of 35 | # documentation for member functions (e.g., Foo::Foo_ctor()). 36 | # Specifically, the files with documentation (e.g., header.dox) must be 37 | # listed *after* the referenced code (e.g., header.h). 38 | #--------------------------------------------------------------------------- 39 | INPUT = \ 40 | main.dox \ 41 | $(SPEXYGEN)/spexygen-awesome/help.dox 42 | 43 | @INCLUDE = spex/Spexyinc 44 | 45 | ENABLED_SECTIONS = 46 | OPTIMIZE_OUTPUT_FOR_C = YES 47 | 48 | RECURSIVE = NO 49 | EXCLUDE = 50 | EXCLUDE_SYMLINKS = NO 51 | EXCLUDE_PATTERNS = 52 | EXCLUDE_SYMBOLS = 53 | EXAMPLE_PATH = 54 | EXAMPLE_PATTERNS = * 55 | EXAMPLE_RECURSIVE = NO 56 | IMAGE_PATH = $(SPEXYGEN)/spexygen-awesome/img 57 | #--------------------------------------------------------------------------- 58 | # Configuration options related to source browsing 59 | #--------------------------------------------------------------------------- 60 | SOURCE_BROWSER = YES 61 | INLINE_SOURCES = NO 62 | STRIP_CODE_COMMENTS = NO 63 | #--------------------------------------------------------------------------- 64 | # Configuration options related to the HTML output 65 | #--------------------------------------------------------------------------- 66 | GENERATE_HTML = YES 67 | HTML_OUTPUT = ./html 68 | HTML_FILE_EXTENSION = .html 69 | HTML_HEADER = $(SPEXYGEN)/spexygen-awesome/spexy-header.html 70 | HTML_FOOTER = $(SPEXYGEN)/spexygen-awesome/spexy-footer.html 71 | HTML_STYLESHEET = 72 | #Spexygen HTML_EXTRA_STYLESHEET = 73 | #Spexygen HTML_EXTRA_FILES = 74 | HTML_COLORSTYLE = DARK 75 | HTML_COLORSTYLE_HUE = 209 76 | HTML_COLORSTYLE_SAT = 255 77 | HTML_COLORSTYLE_GAMMA = 113 78 | HTML_DYNAMIC_MENUS = YES 79 | HTML_DYNAMIC_SECTIONS = NO 80 | HTML_CODE_FOLDING = YES 81 | HTML_COPY_CLIPBOARD = YES 82 | HTML_PROJECT_COOKIE = 83 | HTML_INDEX_NUM_ENTRIES = 100 84 | GENERATE_DOCSET = NO 85 | DOCSET_FEEDNAME = 86 | DOCSET_FEEDURL = 87 | DOCSET_BUNDLE_ID = com.state-machine.doc 88 | DOCSET_PUBLISHER_ID = com.state-machine.doc 89 | DOCSET_PUBLISHER_NAME = QuantumLeaps 90 | GENERATE_HTMLHELP = NO 91 | CHM_FILE = 92 | HHC_LOCATION = 93 | GENERATE_CHI = NO 94 | CHM_INDEX_ENCODING = 95 | BINARY_TOC = NO 96 | TOC_EXPAND = NO 97 | SITEMAP_URL = 98 | GENERATE_QHP = NO 99 | QCH_FILE = 100 | QHP_NAMESPACE = com.state-machine.qp 101 | QHP_VIRTUAL_FOLDER = doc 102 | QHP_CUST_FILTER_NAME = 103 | QHP_CUST_FILTER_ATTRS = 104 | QHP_SECT_FILTER_ATTRS = 105 | QHG_LOCATION = 106 | GENERATE_ECLIPSEHELP = NO 107 | ECLIPSE_DOC_ID = com.state-machine.qp 108 | DISABLE_INDEX = NO 109 | GENERATE_TREEVIEW = YES 110 | FULL_SIDEBAR = NO 111 | ENUM_VALUES_PER_LINE = 4 112 | TREEVIEW_WIDTH = 335 113 | EXT_LINKS_IN_WINDOW = NO 114 | OBFUSCATE_EMAILS = NO 115 | HTML_FORMULA_FORMAT = png 116 | FORMULA_FONTSIZE = 10 117 | FORMULA_MACROFILE = 118 | USE_MATHJAX = NO 119 | MATHJAX_VERSION = MathJax_2 120 | MATHJAX_FORMAT = HTML-CSS 121 | MATHJAX_RELPATH = 122 | MATHJAX_EXTENSIONS = 123 | MATHJAX_CODEFILE = 124 | SEARCHENGINE = YES 125 | SERVER_BASED_SEARCH = NO 126 | EXTERNAL_SEARCH = NO 127 | SEARCHENGINE_URL = 128 | SEARCHDATA_FILE = searchdata.xml 129 | EXTERNAL_SEARCH_ID = Spexygen 130 | EXTRA_SEARCH_MAPPINGS = 131 | #--------------------------------------------------------------------------- 132 | # Configuration options related to the LaTeX output 133 | #--------------------------------------------------------------------------- 134 | GENERATE_LATEX = NO 135 | LATEX_OUTPUT = ./latex 136 | LATEX_CMD_NAME = latex 137 | LATEX_HEADER = $(SPEXYGEN)/spexygen-awesome/spexy-header.tex 138 | LATEX_FOOTER = $(SPEXYGEN)/spexygen-awesome/spexy-footer.tex 139 | MAKEINDEX_CMD_NAME = makeindex 140 | LATEX_MAKEINDEX_CMD = makeindex 141 | COMPACT_LATEX = NO 142 | PAPER_TYPE = letter 143 | EXTRA_PACKAGES = 144 | #--------------------------------------------------------------------------- 145 | # Configuration options related to the preprocessor 146 | #--------------------------------------------------------------------------- 147 | ENABLE_PREPROCESSING = YES 148 | MACRO_EXPANSION = NO 149 | EXPAND_ONLY_PREDEF = NO 150 | SEARCH_INCLUDES = YES 151 | INCLUDE_PATH = 152 | INCLUDE_FILE_PATTERNS = 153 | PREDEFINED = 154 | EXPAND_AS_DEFINED = 155 | SKIP_FUNCTION_MACROS = NO 156 | #--------------------------------------------------------------------------- 157 | # Configuration options related to external references 158 | #--------------------------------------------------------------------------- 159 | DOT_PATH = C:/tools/graphviz/bin 160 | -------------------------------------------------------------------------------- /example/Doxyfile-PDF: -------------------------------------------------------------------------------- 1 | @INCLUDE = Doxyfile 2 | 3 | #--------------------------------------------------------------------------- 4 | # Configuration options related to the LateX output 5 | #--------------------------------------------------------------------------- 6 | PROJECT_NAME = Example 7 | PROJECT_BRIEF = For Spexygen 2.2.5 8 | PROJECT_NUMBER = Document: DOC_MAN_EXA 9 | 10 | #--------------------------------------------------------------------------- 11 | # Project related configuration options 12 | #--------------------------------------------------------------------------- 13 | GENERATE_HTML = NO 14 | GENERATE_LATEX = YES 15 | 16 | ENABLED_SECTIONS += LATEX 17 | 18 | #--------------------------------------------------------------------------- 19 | # Configuration options related to the input files 20 | # 21 | # NOTE: 22 | # The differet INPUT than in Doxyfile is handled in spex-PDF.json 23 | #--------------------------------------------------------------------------- 24 | 25 | # no source code for this document 26 | SOURCE_BROWSER = NO 27 | VERBATIM_HEADERS = NO 28 | -------------------------------------------------------------------------------- /example/inc/header.h: -------------------------------------------------------------------------------- 1 | #ifndef HEADER_H_ 2 | #define HEADER_H_ 3 | 4 | #include 5 | #include 6 | 7 | /*! 8 | @code_uid{free_fun(), Free function brief description} 9 | @code_litem{Details} 10 | Free function longer description. 11 | @param[in] x - the parameter x 12 | @returns pointer to a static array 13 | @code_bw_trace{brief} 14 | - @tr{SRS_EXA_FF_00} 15 | @code_fw_trace 16 | @endcode_uid 17 | */ 18 | uint8_t const *free_fun(uint32_t x); 19 | 20 | /** 21 | @code_uid{Foo, Class Foo brief description} 22 | @code_litem{Details} 23 | Class Foo longer description. 24 | @code_bw_trace{brief} 25 | - @tr{SRS_EXA_Foo_00} 26 | @code_fw_trace 27 | @endcode_uid 28 | */ 29 | typedef struct Foo { 30 | //! @code_uid{Foo::x, Attribute x of class Foo brief description} 31 | //! @code_litem{Details} 32 | //! Attribute x of class Foo: longer description. 33 | //! @code_bw_trace{brief} 34 | //! - @tr{SRS_EXA_Foo_01} 35 | //! @endcode_uid 36 | uint32_t x; 37 | 38 | /// @code_uid{Foo::x_dis, Duplicate Inverse Storage for attribute Foo::x} 39 | /// @code_litem{Details} 40 | /// Duplicate Inverse Storage (DIS) for attribute Foo::x: longer description. 41 | /// @code_bw_trace{brief} 42 | /// - @tr{Foo::x} 43 | /// - @tr{Foo_verify_()} 44 | /// @endcode_uid 45 | uint32_t x_dis; 46 | } Foo; 47 | 48 | /*! 49 | @code_uid{Foo_ctor(), Constructor of class Foo brief description} 50 | @code_litem{Details} 51 | Constructor of class Foo longer description. 52 | @param[in] me - the instance pointer (OOP in C) 53 | @param[in] x - the initial value for me->x 54 | @code_bw_trace{brief} 55 | - @tr{SRS_EXA_Foo_02} 56 | - @tr{Foo} 57 | @code_fw_trace 58 | @endcode_uid 59 | */ 60 | void Foo_ctor(Foo * const me, uint32_t const x); 61 | 62 | /*! 63 | @code_uid{Foo_verify_(), Verify operation to check the class invariant} 64 | @code_litem{Details} 65 | Operation verify_() of class Foo longer description. 66 | @param[in] me - the instance pointer (OOP in C) 67 | @returns 'true' when the Foo instance verification succeeds, 'false' otherwise. 68 | @code_bw_trace{brief} 69 | - @tr{SRS_EXA_Foo_03} 70 | - @tr{Foo} 71 | @code_fw_trace 72 | @endcode_uid 73 | */ 74 | bool Foo_verify_(Foo const * const me); 75 | 76 | /*! 77 | @code_uid{Foo_update_(), Update operation to update the class invariant} 78 | @code_litem{Details} 79 | Constructor of class Foo longer description. 80 | @param[in] me - the instance pointer (OOP in C) 81 | @code_bw_trace{brief} 82 | - @tr{SRS_EXA_Foo_04} 83 | - @tr{Foo} 84 | @code_fw_trace 85 | @endcode_uid 86 | */ 87 | void Foo_update_(Foo* const me); 88 | 89 | /*! 90 | @code_uid{Foo_inst, Foo instance brief description (singleton)} 91 | @code_litem{Details} 92 | Foo instance longer description. 93 | @code_bw_trace{brief} 94 | - @tr{SRS_EXA_Foo_05} 95 | - @tr{Foo} 96 | @code_fw_trace 97 | @endcode_uid 98 | */ 99 | extern Foo const Foo_inst; 100 | 101 | #endif // HEADER_H_ 102 | -------------------------------------------------------------------------------- /example/main.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | @ifnot IN_SPEXYGEN 3 | @mainpage 4 | @section exa About the Example 5 | @else 6 | @page exa Example 7 | @endif 8 | 9 | @ifnot LATEX 10 | @nav_next{srs} 11 |
12 | @endif 13 | 14 | @section about About this Example 15 | Simple example for Doxygen. 16 | 17 | @verbatim 18 | +---example/ <-- this example 19 | | | main.dox <-- example description 20 | | | srs.dox <-- example Software Requirements Specification 21 | | | 22 | | +---inc 23 | | | header.h <-- example header file 24 | | | 25 | | +---src 26 | | | source.c <-- example source file 27 | | | 28 | | \---test 29 | | test1.c <-- example test file 30 | | test2.c <-- example test file 31 | @endverbatim 32 | 33 | @section exa_gen Spexygen-Generated Documentation 34 | 35 | @image html logo_spexygen.png "image caption" 36 | @image latex logo_spexygen.png "image caption" 37 | 38 | The following sections contain the _Spexygen_-generated documentation of this example: 39 | - @subpage srs 40 | - @ref header.h 41 | - @ref source.c 42 | - @ref test1.c 43 | - @ref test2.c 44 | 45 | @note 46 | Please note the generated _forward-traceability_ and the _backward-traceability_ links (augmented with the brief descriptions) in the "work artifacts" and "code artifacts". Also, try clicking on the provided traceability links as well as _searching_ the UIDs in the Doxygen "search" box. 47 | 48 | @ifnot LATEX 49 |

50 | @nav_next{srs} 51 | @endif 52 | */ 53 | -------------------------------------------------------------------------------- /example/make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: =========================================================================== 3 | :: Product: batch script for generating Spexygen documentation 4 | :: 5 | :: Q u a n t u m L e a P s 6 | :: ------------------------ 7 | :: Modern Embedded Software 8 | :: 9 | :: Copyright (C) 2005 Quantum Leaps, LLC . 10 | :: 11 | :: SPDX-License-Identifier: MIT 12 | :: 13 | :: Permission is hereby granted, free of charge, to any person obtaining a copy of 14 | :: this software and associated documentation files (the "Software"), to deal in 15 | :: the Software without restriction, including without limitation the rights to 16 | :: use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 17 | :: the Software, and to permit persons to whom the Software is furnished to do so, 18 | :: subject to the following conditions: 19 | :: 20 | :: The above copyright notice and this permission notice shall be included in all 21 | :: copies or substantial portions of the Software. 22 | :: 23 | :: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | :: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 25 | :: FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 26 | :: COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 27 | :: IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 28 | :: CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | :: 30 | :: 31 | :: 32 | :: =========================================================================== 33 | @setlocal 34 | 35 | @echo usage: 36 | @echo make 37 | @echo make -PDF 38 | 39 | :: tools (adjust to your system)---------------------------------------------- 40 | :: Doxygen/Spexygen tools 41 | @set DOXYGEN=doxygen 42 | @set SPEXYGEN=.. 43 | 44 | @echo Generate Spexygen tracing ---------------------------------------------- 45 | python %SPEXYGEN%/spexygen.py spex.json 46 | 47 | ::============================================================================ 48 | @if "%1"=="-PDF" goto PDF 49 | 50 | @echo Generate HTML Documentation -------------------------------------------- 51 | @set HTML_OUT=html 52 | 53 | @echo. 54 | @echo cleanup 55 | rmdir /S /Q %HTML_OUT% 56 | 57 | @echo generating HTML... 58 | %DOXYGEN% Doxyfile 59 | 60 | @echo Adding custom files... 61 | copy %SPEXYGEN%\spexygen-awesome\jquery.js %HTML_OUT% 62 | 63 | ::qclean %HTML_OUT% 64 | goto END 65 | 66 | :PDF 67 | @echo Generate PDF Documentation -------------------------------------------- 68 | @set LATEX_OUT=latex 69 | 70 | @echo. 71 | @echo cleanup 72 | rmdir /S /Q %LATEX_OUT% 73 | 74 | @echo generating LATEX... 75 | %DOXYGEN% Doxyfile-PDF 76 | 77 | :: Generate LaTex/PDF Documentation... 78 | @echo generating PDF... 79 | @cd %LATEX_OUT% 80 | @call make.bat 81 | @copy refman.pdf ..\DOC_MAN_EXA.pdf 82 | @cd .. 83 | rmdir /S /Q %LATEX_OUT% 84 | 85 | :END 86 | @echo Final cleanup ---------------------------------------------------------- 87 | rmdir /S /Q .\spex 88 | 89 | @endlocal 90 | -------------------------------------------------------------------------------- /example/spex.json: -------------------------------------------------------------------------------- 1 | { 2 | "gen-dir": "spex", 3 | "gen-inc": "Spexyinc", 4 | "gen": [ 5 | "./srs.dox", 6 | "./inc", 7 | "./src", 8 | "./test" 9 | ], 10 | "note1": "no separate 'trace' section, meaning that 'gen' section will be used for tracing" 11 | } -------------------------------------------------------------------------------- /example/src/source.c: -------------------------------------------------------------------------------- 1 | #include "header.h" 2 | 3 | uint8_t const * free_fun(uint32_t x) { 4 | static uint8_t arr[10]; 5 | a[0] = (uint8_t)x; 6 | a[1] = (uint8_t)(x >> 8U); 7 | a[2] = (uint8_t)(x >> 16U); 8 | a[3] = (uint8_t)(x >> 24U); 9 | return arr; 10 | } 11 | 12 | void Foo_ctor(Foo * const me, uint32_t const x) { 13 | me->x = x; 14 | Foo_update_(me); 15 | } 16 | 17 | void Foo_update_(Foo* const me) { 18 | me->x_dis = ~me->x; 19 | } 20 | 21 | bool Foo_verify_(Foo const* const me) { 22 | return me->x_dis == ~me->x; 23 | } 24 | -------------------------------------------------------------------------------- /example/srs.dox: -------------------------------------------------------------------------------- 1 | /*! @page srs Software Requirements Specification 2 | @ifnot LATEX 3 | @nav_prev{exa} 4 |
5 | @endif 6 | 7 | This is an example Software Requirements Specification (SRS), illustrating the typical structure and the use of _Spexygen_ commands to define the __traceable__ requirement work artifacts. 8 | 9 | @note 10 | Please note the generated _forward-traceability_ and the _backward-traceability_ links (augmented with the brief descriptions) in the "work artifacts" and "code artifacts". Also, try clicking on the provided traceability links as well as _searching_ the UIDs in the Doxygen "search" box. 11 | 12 | @section srs-def Concepts & Definitions 13 | Description of concepts and definitions... 14 | 15 | @section srs_req Requirements 16 | Definitions of formal requirements specifications with _Spexygen_ commands. 17 | 18 | @uid{SRS_EXA_FF_00,My project shall provide a free function foo().} 19 | @uid_litem{Description} 20 | Longer description of the requirement 21 | @uid_fw_trace{1} 22 | @enduid 23 | 24 | @uid{SRS_EXA_Foo_00,My project shall provide a class Foo.} 25 | @uid_litem{Description} 26 | Longer description of the requirement 27 | @uid_fw_trace{1} 28 | @enduid 29 | 30 | @uid{SRS_EXA_Foo_01,Class Foo shall provide a public attribute x.} 31 | @uid_litem{Description} 32 | Longer description of the requirement 33 | @uid_bw_trace{brief} 34 | - @tr{SRS_EXA_Foo_00} 35 | @uid_fw_trace{2} 36 | @enduid 37 | 38 | @uid{SRS_EXA_Foo_02,Class Foo shall provide a constructor.} 39 | @uid_litem{Description} 40 | Longer description of the requirement 41 | @uid_bw_trace{brief} 42 | - @tr{SRS_EXA_Foo_00} 43 | @uid_fw_trace{2} 44 | @enduid 45 | 46 | @uid{SRS_EXA_Foo_03,Class Foo shall provide a verify operation.} 47 | @uid_litem{Description} 48 | Longer description of the requirement 49 | @uid_bw_trace{brief} 50 | - @tr{SRS_EXA_Foo_00} 51 | @uid_fw_trace 52 | @enduid 53 | 54 | @uid{SRS_EXA_Foo_04,Class Foo shall provide an update operation.} 55 | @uid_litem{Description} 56 | Longer description of the requirement 57 | @uid_bw_trace{brief} 58 | - @tr{SRS_EXA_Foo_00} 59 | @uid_fw_trace 60 | @enduid 61 | 62 | @uid{SRS_EXA_Foo_05,Class Foo shall be a singleton.} 63 | @uid_litem{Description} 64 | Longer description of the requirement 65 | @uid_bw_trace{brief} 66 | - @tr{SRS_EXA_Foo_00} 67 | @uid_fw_trace 68 | @enduid 69 | 70 | @ifnot LATEX 71 |
72 | @nav_prev{exa} 73 | @endif 74 | */ 75 | -------------------------------------------------------------------------------- /example/test/test1.c: -------------------------------------------------------------------------------- 1 | #include "header.h" 2 | #include "unity.h" 3 | 4 | void setUp(void) { 5 | } 6 | 7 | void tearDown(void) { 8 | } 9 | 10 | /*! 11 | @code_uid{TUN_PRJ_free_fun_00, zero input test} 12 | @code_litem{Details} 13 | This test checks that zero input to free_fun() produces zero array. 14 | @code_bw_trace{brief} 15 | - @tr{free_fun()} 16 | @endcode_uid 17 | */ 18 | void TUN_PRJ_free_fun_00(void) { 19 | uint8_t const *result = free_fun(0U); 20 | TEST_ASSERT_EQUAL_UINT8(0, result[0]); 21 | TEST_ASSERT_EQUAL_UINT8(0, result[1]); 22 | TEST_ASSERT_EQUAL_UINT8(0, result[2]); 23 | TEST_ASSERT_EQUAL_UINT8(0, result[3]); 24 | } 25 | 26 | /*! 27 | @code_uid{TUN_PRJ_free_fun_01, non-zero input test} 28 | @code_litem{Details} 29 | This test checks that non-zero input to free_fun() produces expected array. 30 | @code_bw_trace 31 | - @tr{free_fun()} 32 | @endcode_uid 33 | */ 34 | void TUN_PRJ_free_fun_01(void) { 35 | uint8_t const* result = free_fun(0x1A2B3C4CU); 36 | TEST_ASSERT_EQUAL_UINT8(0x1A, result[0]); 37 | TEST_ASSERT_EQUAL_UINT8(0x2B, result[1]); 38 | TEST_ASSERT_EQUAL_UINT8(0x3C, result[2]); 39 | TEST_ASSERT_EQUAL_UINT8(0x4D, result[3]); 40 | } 41 | -------------------------------------------------------------------------------- /example/test/test2.c: -------------------------------------------------------------------------------- 1 | #include "header.h" 2 | #include "unity.h" 3 | 4 | void setUp(void) { 5 | } 6 | 7 | void tearDown(void) { 8 | } 9 | 10 | /*! 11 | @code_uid{TUN_PRJ_Foo_ctor_01, constructor test} 12 | @code_litem{Details} 13 | This test checks that Foo_ctor() produces valid instance. 14 | @code_bw_trace{brief} 15 | - @tr{Foo_ctor()} 16 | @endcode_uid 17 | */ 18 | void TUN_PRJ_Foo_ctor_01(void) { 19 | Foo_ctor(&Foo_inst, 0x12345678); 20 | TEST_ASSERT_EQUAL_INT32(0x12345678, Foo_inst.x); 21 | TEST_ASSERT_EQUAL_INT32(~0x12345678, Foo_inst.x_dis); 22 | } 23 | 24 | /*! 25 | @code_uid{TUN_PRJ_Foo_verify_00, verify positive test} 26 | @code_litem{Details} 27 | This test checks that Foo_verify_() distinguishes valid instance. 28 | @code_bw_trace{brief} 29 | - @tr{Foo_verify_()} 30 | @endcode_uid 31 | */ 32 | void TUN_PRJ_Foo_verify_00(void) { 33 | Foo_ctor(&Foo_inst, 0x12345678); 34 | TEST_ASSERT_TRUE(Foo_verify_(&Foo_inst)); 35 | } 36 | 37 | /*! 38 | @code_uid{TUN_PRJ_Foo_verify_01, verify negative test} 39 | @code_litem{Details} 40 | This test checks that Foo_verify_() distinguishes invalid instance. 41 | @code_bw_trace{brief} 42 | - @tr{Foo_verify_()} 43 | @endcode_uid 44 | */ 45 | void TUN_PRJ_Foo_verify_01(void) { 46 | Foo_ctor(&Foo_inst, 0x12345678); 47 | Foo_inst.x -= 1; // change x 48 | TEST_ASSERT_FALSE(Foo_verify_(&Foo_inst)); 49 | } 50 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Setup script. 4 | To build _spexygen_ install: 5 | [sudo] python setup.py sdist bdist_wheel 6 | ''' 7 | 8 | from setuptools import setup 9 | 10 | setup( 11 | name="spexygen", 12 | version="2.2.5", 13 | author="Quantum Leaps", 14 | author_email="info@state-machine.com", 15 | description="Traceable specifications based on doxygen", 16 | long_description=open("README.md").read(), 17 | long_description_content_type="text/markdown", 18 | url="https://github.com/QuantumLeaps/spexygen", 19 | license="MIT", 20 | platforms="any", 21 | py_modules=["spexygen"], 22 | entry_points={"console_scripts": ["spexygen = spexygen:main"]}, 23 | classifiers=["Development Status :: 5 - Production/Stable", 24 | "Intended Audience :: Developers", 25 | "Topic :: Software Development :: Documentation", 26 | "License :: OSI Approved :: MIT License", 27 | "Operating System :: Microsoft :: Windows", 28 | "Operating System :: POSIX :: Linux", 29 | "Operating System :: MacOS :: MacOS X", 30 | "Programming Language :: Python", 31 | "Programming Language :: Python :: 3"], 32 | ) 33 | -------------------------------------------------------------------------------- /spexygen-awesome/doxygen-awesome-darkmode-toggle.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2021 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeDarkModeToggle extends HTMLElement { 31 | // SVG icons from https://fonts.google.com/icons 32 | // Licensed under the Apache 2.0 license: 33 | // https://www.apache.org/licenses/LICENSE-2.0.html 34 | static lightModeIcon = `` 35 | static darkModeIcon = `` 36 | static title = "Toggle Light/Dark Mode" 37 | 38 | static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode" 39 | static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode" 40 | 41 | static _staticConstructor = function() { 42 | DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference) 43 | // Update the color scheme when the browsers preference changes 44 | // without user interaction on the website. 45 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { 46 | DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() 47 | }) 48 | // Update the color scheme when the tab is made visible again. 49 | // It is possible that the appearance was changed in another tab 50 | // while this tab was in the background. 51 | document.addEventListener("visibilitychange", visibilityState => { 52 | if (document.visibilityState === 'visible') { 53 | DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() 54 | } 55 | }); 56 | }() 57 | 58 | static init() { 59 | $(function() { 60 | $(document).ready(function() { 61 | const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle') 62 | toggleButton.title = DoxygenAwesomeDarkModeToggle.title 63 | toggleButton.updateIcon() 64 | 65 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { 66 | toggleButton.updateIcon() 67 | }) 68 | document.addEventListener("visibilitychange", visibilityState => { 69 | if (document.visibilityState === 'visible') { 70 | toggleButton.updateIcon() 71 | } 72 | }); 73 | 74 | $(document).ready(function(){ 75 | document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) 76 | }) 77 | $(window).resize(function(){ 78 | document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) 79 | }) 80 | }) 81 | }) 82 | } 83 | 84 | constructor() { 85 | super(); 86 | this.onclick=this.toggleDarkMode 87 | } 88 | 89 | /** 90 | * @returns `true` for dark-mode, `false` for light-mode system preference 91 | */ 92 | static get systemPreference() { 93 | return window.matchMedia('(prefers-color-scheme: dark)').matches 94 | } 95 | 96 | /** 97 | * @returns `true` for dark-mode, `false` for light-mode user preference 98 | */ 99 | static get userPreference() { 100 | return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) || 101 | (DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)) 102 | } 103 | 104 | static set userPreference(userPreference) { 105 | DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference 106 | if(!userPreference) { 107 | if(DoxygenAwesomeDarkModeToggle.systemPreference) { 108 | localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true) 109 | } else { 110 | localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey) 111 | } 112 | } else { 113 | if(!DoxygenAwesomeDarkModeToggle.systemPreference) { 114 | localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true) 115 | } else { 116 | localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey) 117 | } 118 | } 119 | DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged() 120 | } 121 | 122 | static enableDarkMode(enable) { 123 | if(enable) { 124 | DoxygenAwesomeDarkModeToggle.darkModeEnabled = true 125 | document.documentElement.classList.add("dark-mode") 126 | document.documentElement.classList.remove("light-mode") 127 | } else { 128 | DoxygenAwesomeDarkModeToggle.darkModeEnabled = false 129 | document.documentElement.classList.remove("dark-mode") 130 | document.documentElement.classList.add("light-mode") 131 | } 132 | } 133 | 134 | static onSystemPreferenceChanged() { 135 | DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference 136 | DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) 137 | } 138 | 139 | static onUserPreferenceChanged() { 140 | DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) 141 | } 142 | 143 | toggleDarkMode() { 144 | DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference 145 | this.updateIcon() 146 | } 147 | 148 | updateIcon() { 149 | if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) { 150 | this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon 151 | } else { 152 | this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon 153 | } 154 | } 155 | } 156 | 157 | customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle); 158 | -------------------------------------------------------------------------------- /spexygen-awesome/doxygen-awesome-fragment-copy-button.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2022 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeFragmentCopyButton extends HTMLElement { 31 | constructor() { 32 | super(); 33 | this.onclick=this.copyContent 34 | } 35 | static title = "Copy to clipboard" 36 | static copyIcon = `` 37 | static successIcon = `` 38 | static successDuration = 980 39 | static init() { 40 | $(function() { 41 | $(document).ready(function() { 42 | if(navigator.clipboard) { 43 | const fragments = document.getElementsByClassName("fragment") 44 | for(const fragment of fragments) { 45 | const fragmentWrapper = document.createElement("div") 46 | fragmentWrapper.className = "doxygen-awesome-fragment-wrapper" 47 | const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button") 48 | fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon 49 | fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title 50 | 51 | fragment.parentNode.replaceChild(fragmentWrapper, fragment) 52 | fragmentWrapper.appendChild(fragment) 53 | fragmentWrapper.appendChild(fragmentCopyButton) 54 | 55 | } 56 | } 57 | }) 58 | }) 59 | } 60 | 61 | 62 | copyContent() { 63 | const content = this.previousSibling.cloneNode(true) 64 | // filter out line number from file listings 65 | content.querySelectorAll(".lineno, .ttc").forEach((node) => { 66 | node.remove() 67 | }) 68 | let textContent = content.textContent 69 | // remove trailing newlines that appear in file listings 70 | let numberOfTrailingNewlines = 0 71 | while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') { 72 | numberOfTrailingNewlines++; 73 | } 74 | textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines) 75 | navigator.clipboard.writeText(textContent); 76 | this.classList.add("success") 77 | this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon 78 | window.setTimeout(() => { 79 | this.classList.remove("success") 80 | this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon 81 | }, DoxygenAwesomeFragmentCopyButton.successDuration); 82 | } 83 | } 84 | 85 | customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton) 86 | -------------------------------------------------------------------------------- /spexygen-awesome/doxygen-awesome-interactive-toc.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2022 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeInteractiveToc { 31 | static topOffset = 38 32 | static hideMobileMenu = true 33 | static headers = [] 34 | 35 | static init() { 36 | window.addEventListener("load", () => { 37 | let toc = document.querySelector(".contents > .toc") 38 | if(toc) { 39 | toc.classList.add("interactive") 40 | if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) { 41 | toc.classList.add("open") 42 | } 43 | document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => { 44 | if(toc.classList.contains("open")) { 45 | toc.classList.remove("open") 46 | } else { 47 | toc.classList.add("open") 48 | } 49 | }) 50 | 51 | document.querySelectorAll(".contents > .toc > ul a").forEach((node) => { 52 | let id = node.getAttribute("href").substring(1) 53 | DoxygenAwesomeInteractiveToc.headers.push({ 54 | node: node, 55 | headerNode: document.getElementById(id) 56 | }) 57 | 58 | document.getElementById("doc-content")?.addEventListener("scroll", () => { 59 | DoxygenAwesomeInteractiveToc.update() 60 | }) 61 | }) 62 | DoxygenAwesomeInteractiveToc.update() 63 | } 64 | }) 65 | } 66 | 67 | static update() { 68 | let active = DoxygenAwesomeInteractiveToc.headers[0]?.node 69 | DoxygenAwesomeInteractiveToc.headers.forEach((header) => { 70 | let position = header.headerNode.getBoundingClientRect().top 71 | header.node.classList.remove("active") 72 | header.node.classList.remove("aboveActive") 73 | if(position < DoxygenAwesomeInteractiveToc.topOffset) { 74 | active = header.node 75 | active?.classList.add("aboveActive") 76 | } 77 | }) 78 | active?.classList.add("active") 79 | active?.classList.remove("aboveActive") 80 | } 81 | } -------------------------------------------------------------------------------- /spexygen-awesome/doxygen-awesome-paragraph-link.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2022 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeParagraphLink { 31 | // Icon from https://fonts.google.com/icons 32 | // Licensed under the Apache 2.0 license: 33 | // https://www.apache.org/licenses/LICENSE-2.0.html 34 | static icon = `` 35 | static title = "Permanent Link" 36 | static init() { 37 | $(function() { 38 | $(document).ready(function() { 39 | document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => { 40 | let anchorlink = document.createElement("a") 41 | anchorlink.setAttribute("href", `#${node.getAttribute("id")}`) 42 | anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title) 43 | anchorlink.classList.add("anchorlink") 44 | node.classList.add("anchor") 45 | anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon 46 | node.parentElement.appendChild(anchorlink) 47 | }) 48 | }) 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spexygen-awesome/doxygen-awesome-sidebar-only-darkmode-toggle.css: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | 4 | Doxygen Awesome 5 | https://github.com/jothepro/doxygen-awesome-css 6 | 7 | MIT License 8 | 9 | Copyright (c) 2021 - 2023 jothepro 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | 29 | */ 30 | 31 | @media screen and (min-width: 768px) { 32 | 33 | #MSearchBox { 34 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px); 35 | } 36 | 37 | #MSearchField { 38 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /spexygen-awesome/doxygen-awesome-sidebar-only.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2021 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | html { 31 | /* side nav width. MUST be = `TREEVIEW_WIDTH`. 32 | * Make sure it is wide enough to contain the page title (logo + title + version) 33 | */ 34 | --side-nav-fixed-width: 335px; 35 | --menu-display: none; 36 | 37 | --top-height: 120px; 38 | --toc-sticky-top: -25px; 39 | --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px); 40 | } 41 | 42 | #projectname { 43 | white-space: nowrap; 44 | } 45 | 46 | 47 | @media screen and (min-width: 768px) { 48 | html { 49 | --searchbar-background: var(--page-background-color); 50 | } 51 | 52 | #side-nav { 53 | min-width: var(--side-nav-fixed-width); 54 | max-width: var(--side-nav-fixed-width); 55 | top: var(--top-height); 56 | overflow: visible; 57 | } 58 | 59 | #nav-tree, #side-nav { 60 | height: calc(100vh - var(--top-height)) !important; 61 | } 62 | 63 | #nav-tree { 64 | padding: 0; 65 | } 66 | 67 | #top { 68 | display: block; 69 | border-bottom: none; 70 | height: var(--top-height); 71 | margin-bottom: calc(0px - var(--top-height)); 72 | max-width: var(--side-nav-fixed-width); 73 | overflow: hidden; 74 | background: var(--side-nav-background); 75 | } 76 | #main-nav { 77 | float: left; 78 | padding-right: 0; 79 | } 80 | 81 | .ui-resizable-handle { 82 | cursor: default; 83 | width: 1px !important; 84 | background: var(--separator-color); 85 | box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color); 86 | } 87 | 88 | #nav-path { 89 | position: fixed; 90 | right: 0; 91 | left: var(--side-nav-fixed-width); 92 | bottom: 0; 93 | width: auto; 94 | } 95 | 96 | #doc-content { 97 | height: calc(100vh - 31px) !important; 98 | padding-bottom: calc(3 * var(--spacing-large)); 99 | padding-top: calc(var(--top-height) - 80px); 100 | box-sizing: border-box; 101 | margin-left: var(--side-nav-fixed-width) !important; 102 | } 103 | 104 | #MSearchBox { 105 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium))); 106 | } 107 | 108 | #MSearchField { 109 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px); 110 | } 111 | 112 | #MSearchResultsWindow { 113 | left: var(--spacing-medium) !important; 114 | right: auto; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /spexygen-awesome/doxygen-awesome-tabs.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeTabs { 31 | 32 | static init() { 33 | window.addEventListener("load", () => { 34 | document.querySelectorAll(".tabbed:not(:empty)").forEach((tabbed, tabbedIndex) => { 35 | let tabLinkList = [] 36 | tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => { 37 | tab.id = "tab_" + tabbedIndex + "_" + tabIndex 38 | let header = tab.querySelector(".tab-title") 39 | let tabLink = document.createElement("button") 40 | tabLink.classList.add("tab-button") 41 | tabLink.appendChild(header) 42 | header.title = header.textContent 43 | tabLink.addEventListener("click", () => { 44 | tabbed.querySelectorAll(":scope > ul > li").forEach((tab) => { 45 | tab.classList.remove("selected") 46 | }) 47 | tabLinkList.forEach((tabLink) => { 48 | tabLink.classList.remove("active") 49 | }) 50 | tab.classList.add("selected") 51 | tabLink.classList.add("active") 52 | }) 53 | tabLinkList.push(tabLink) 54 | if(tabIndex == 0) { 55 | tab.classList.add("selected") 56 | tabLink.classList.add("active") 57 | } 58 | }) 59 | let tabsOverview = document.createElement("div") 60 | tabsOverview.classList.add("tabs-overview") 61 | let tabsOverviewContainer = document.createElement("div") 62 | tabsOverviewContainer.classList.add("tabs-overview-container") 63 | tabLinkList.forEach((tabLink) => { 64 | tabsOverview.appendChild(tabLink) 65 | }) 66 | tabsOverviewContainer.appendChild(tabsOverview) 67 | tabbed.before(tabsOverviewContainer) 68 | 69 | function resize() { 70 | let maxTabHeight = 0 71 | tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => { 72 | let visibility = tab.style.display 73 | tab.style.display = "block" 74 | maxTabHeight = Math.max(tab.offsetHeight, maxTabHeight) 75 | tab.style.display = visibility 76 | }) 77 | tabbed.style.height = `${maxTabHeight + 10}px` 78 | } 79 | 80 | resize() 81 | new ResizeObserver(resize).observe(tabbed) 82 | }) 83 | }) 84 | 85 | } 86 | 87 | static resize(tabbed) { 88 | 89 | } 90 | } -------------------------------------------------------------------------------- /spexygen-awesome/help.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | @ifnot LATEX 3 | 4 | @page help Using Online Help 5 | 6 |

You can use this online help in various ways: either sequentially from beginning to end or just by quickly locating the interesting topic. 7 |

8 | 9 | [![](help_using.jpg)](https://github.com/QuantumLeaps/spexygen) 10 | @caption{Online Help Features} 11 | 12 | @section help_dark_mode Dark Mode Toggle 13 | You can toggle the Dark/Light Mode of the help display with the button in the top-left corner, below the logo. 14 | 15 | 16 | The Light display mode is selected 17 | 18 | 19 | The Dark display mode is selected 20 | 21 | @section help_tree_view Using the Tree View 22 | The *Tree View* pane on the left-hand side of the browser displays the hierarchical Table of Contents, which can be either **linked-to** or **unlinked-from** the *Current Topic* displayed on the right-hand side. You can toggle between the two modes by pressing the **Link/Unlink Current Topic** icon at the top of the *Tree View* pane. 23 | 24 | 25 | When the *Tree View* is **linked** to the Current Topic, the *Tree View* will always follow the currently viewed topic, by expanding and highlighting the pertinent section of the hierarchical Table of Contents. 26 | 27 |
28 | 29 | When the *Tree View* is **unlinked** from the Current Topic, the *Tree View* will show only the explicitly selected section of the hierarchical Table of Contents and will **not** follow the topics activated by internal documentation links. 30 | 31 | @section help_seq Reading Online Help Sequentially 32 | You can move from topic to topic by means of the Next: link at the bottom of each page. 33 | 34 | @section help_random Quickly Locating a Topic of Interest 35 | You can use the following elements: 36 | - the @ref help_tree_view "Tree View" pane on the left-hand side of the browser; 37 | - the **Table of Contents** box in the top-right corner of the page; 38 | - the **Search** box in the upper-right corner of the browser window 39 | 40 | @section hel_spexygen Spexygen on GitHub 41 | If you like this styling for doxygen, it is based on @webref{https://github.com/jothepro/doxygen-awesome-css,doxygen-awesome-css}. The styling with modifications made by Quantum Leaps is available as part of the @webref{https://github.com/QuantumLeaps/spexygen,spexyen documentation system}. 42 | 43 | [![Spexygen on GitHub](logo_spexygen.webp)](https://github.com/QuantumLeaps/spexygen) 44 | 45 | @endif 46 | */ 47 | -------------------------------------------------------------------------------- /spexygen-awesome/image-preview.js: -------------------------------------------------------------------------------- 1 | /* Image preview script 2 | * powered by jQuery (http://www.jquery.com) 3 | * written by Alen Grakalic (http://cssglobe.com) 4 | * for more info visit 5 | * http://cssglobe.com/post/1695/easiest-tooltip-and-image-preview-using-jquery 6 | */ 7 | this.imagePreview = function(){ 8 | // you might want to adjust to get the right result 9 | horOffset = -100; 10 | verOffset = 0; 11 | 12 | /* END CONFIG */ 13 | $("a.preview").hover(function(e){ 14 | this.t = this.title; 15 | this.title = ""; 16 | var c = (this.t != "") ? "
" + this.t : ""; 17 | $("body").append("

Image preview"+ c +"

"); 18 | $("#preview") 19 | .css("top",(e.pageY - verOffset) + "px") 20 | .css("left",(e.pageX + horOffset) + "px") 21 | .fadeIn("fast"); 22 | }, 23 | function(){ 24 | this.title = this.t; 25 | $("#preview").remove(); 26 | }); 27 | $("a.preview").mousemove(function(e){ 28 | $("#preview") 29 | .css("top",(e.pageY - verOffset) + "px") 30 | .css("left",(e.pageX + horOffset) + "px"); 31 | }); 32 | }; 33 | // starting the script on page load 34 | $(document).ready(function(){ 35 | imagePreview(); 36 | }); -------------------------------------------------------------------------------- /spexygen-awesome/img/banner_spexygen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/banner_spexygen.jpg -------------------------------------------------------------------------------- /spexygen-awesome/img/banner_spexygen.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/banner_spexygen.webp -------------------------------------------------------------------------------- /spexygen-awesome/img/help_dark.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/help_dark.webp -------------------------------------------------------------------------------- /spexygen-awesome/img/help_light.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/help_light.webp -------------------------------------------------------------------------------- /spexygen-awesome/img/help_using.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/help_using.jpg -------------------------------------------------------------------------------- /spexygen-awesome/img/logo_spexygen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/logo_spexygen.png -------------------------------------------------------------------------------- /spexygen-awesome/img/logo_spexygen.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/logo_spexygen.webp -------------------------------------------------------------------------------- /spexygen-awesome/img/tree-view_linked.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/tree-view_linked.webp -------------------------------------------------------------------------------- /spexygen-awesome/img/tree-view_unlinked.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantumLeaps/spexygen/ca40f78754845478ba9890c939c9d2bb6fdf2444/spexygen-awesome/img/tree-view_unlinked.webp -------------------------------------------------------------------------------- /spexygen-awesome/spexy-awesome.css: -------------------------------------------------------------------------------- 1 | /******** Overrides from doxygen-awesome *****/ 2 | html { 3 | --side-nav-fixed-width: 320px; 4 | --side-top-background: #ecf0f5; 5 | --side-nav-arrow-opacity: 1; 6 | --side-nav-background-hover: #E0E0E0; 7 | --top-height: 210px; 8 | --remark-color: #eeeeee; 9 | --remark-color-dark: #777777; 10 | --remark-color-darker: #333333; 11 | } 12 | 13 | @media (prefers-color-scheme: dark) { 14 | html:not(.light-mode) { 15 | --remark-color: #555555; 16 | --remark-color-dark: #aaaaaa; 17 | --remark-color-darker: #eeeeee; 18 | } 19 | } 20 | 21 | html.dark-mode { 22 | --remark-color: #555555; 23 | --remark-color-dark: #aaaaaa; 24 | --remark-color-darker: #eeeeee; 25 | } 26 | 27 | p { 28 | margin-block-end: 0.25em; 29 | } 30 | 31 | ul { 32 | margin-block-start: 0.25em; 33 | } 34 | 35 | dl.remark { 36 | background: var(--remark-color); 37 | border-left: 8px solid var(--remark-color-dark); 38 | color: var(--remark-color-darker); 39 | } 40 | 41 | dl.remark dt { 42 | color: var(--remark-color-dark); 43 | } 44 | 45 | 46 | #top { 47 | background: var(--side-top-background); 48 | } 49 | 50 | @media (prefers-color-scheme: dark) { 51 | html:not(.light-mode) { 52 | --side-top-background: #353638; 53 | --side-nav-background-hover: #000000; 54 | } 55 | } 56 | 57 | html.dark-mode { 58 | --side-top-background: #353638; 59 | --side-nav-background-hover: #000000; 60 | } 61 | 62 | #projectlogo { 63 | text-align: left; 64 | } 65 | 66 | #projectname { 67 | font-size: 200%; 68 | line-height: normal; 69 | } 70 | 71 | #projectnumber { 72 | font-size: 80%; 73 | } 74 | 75 | #projectbrief { 76 | font-size: 120%; 77 | } 78 | 79 | #doc-content { 80 | padding-top: 0; 81 | border-left: 1px solid var(--separator-color); 82 | } 83 | 84 | div.header .summary { 85 | font-size: inherit; 86 | } 87 | 88 | div.ingroups { 89 | font-size: 0.5em; 90 | } 91 | 92 | #nav-tree .arrow { 93 | font-size: 0.8em; 94 | } 95 | 96 | #nav-tree .selected { 97 | font-size: 0.8em; 98 | background-color: var(--side-top-background); 99 | } 100 | 101 | #nav-tree .item:hover { 102 | background-color: var(--side-nav-background-hover); 103 | } 104 | 105 | #nav-path { 106 | border-left: 1px solid var(--separator-color); 107 | } 108 | 109 | dl.post { 110 | background: var(--invariant-color); 111 | border-left: 8px solid var(--invariant-color-dark); 112 | color: var(--invariant-color-darker); 113 | padding: var(--spacing-medium); 114 | margin: var(--spacing-medium) 0; 115 | overflow: hidden; 116 | margin-left: 0; 117 | border-radius: var(--border-radius-small); 118 | } 119 | 120 | /***** from Custom.css *********/ 121 | .next_button { 122 | display: inline; 123 | padding: var(--spacing-large) 0 var(--spacing-small) 0; 124 | user-select: none; 125 | } 126 | 127 | .next_button::after { 128 | /* clearfix */ 129 | content: ""; 130 | clear: both; 131 | display: table; 132 | } 133 | 134 | .next_button a { 135 | overflow: hidden; 136 | float: right; 137 | border: 1px solid var(--separator-color); 138 | padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large); 139 | border-radius: var(--border-radius-medium); 140 | color: var(--page-secondary-foreground-color) !important; 141 | text-decoration: none; 142 | background-color: var(--side-nav-background); 143 | transition: color .08s ease-in-out, background-color .1s ease-in-out; 144 | } 145 | 146 | .next_button a:hover { 147 | color: var(--page-foreground-color) !important; 148 | background-color: var(--side-top-background); 149 | } 150 | 151 | .next_button a::after { 152 | content: '〉'; 153 | color: var(--page-secondary-foreground-color) !important; 154 | padding-left: var(--spacing-large); 155 | display: inline-block; 156 | transition: color .08s ease-in-out, transform .09s ease-in-out; 157 | } 158 | 159 | .next_button a:hover::after { 160 | color: var(--page-foreground-color) !important; 161 | transform: translateX(3px); 162 | } 163 | 164 | .prev_button { 165 | display: inline; 166 | padding: var(--spacing-large) 0 var(--spacing-small) 0; 167 | color: var(--page-background-color); 168 | user-select: none; 169 | } 170 | 171 | .prev_button a { 172 | overflow: hidden; 173 | float: left; 174 | border: 1px solid var(--separator-color); 175 | padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large); 176 | border-radius: var(--border-radius-medium); 177 | color: var(--page-secondary-foreground-color) !important; 178 | text-decoration: none; 179 | background-color: var(--side-nav-background); 180 | transition: color .08s ease-in-out, background-color .1s ease-in-out; 181 | } 182 | 183 | .prev_button a:hover { 184 | color: var(--page-foreground-color) !important; 185 | background-color: var(--side-top-background); 186 | } 187 | 188 | .prev_button a::before { 189 | content: '〈'; 190 | color: var(--page-secondary-foreground-color) !important; 191 | padding-right: var(--spacing-large); 192 | display: inline-block; 193 | transition: color .08s ease-in-out, transform .09s ease-in-out; 194 | } 195 | 196 | .prev_button a:hover::before { 197 | color: var(--page-foreground-color) !important; 198 | transform: translateX(-3px); 199 | } 200 | 201 | html.dark-mode #variants_image img { 202 | filter: brightness(87%) hue-rotate(180deg) invert(); 203 | } 204 | 205 | /******** Custom spans ********/ 206 | .img { 207 | padding-left: 20px; 208 | white-space: nowrap; 209 | color: #00A; 210 | } 211 | 212 | .logo a { 213 | line-height: 50px; 214 | padding: 10px 0px 20px 55px; 215 | white-space: nowrap; 216 | color: #007; 217 | font-weight: bold; 218 | } 219 | 220 | table.item { 221 | text-align: left; 222 | width: 100%; 223 | } 224 | 225 | table.item th { 226 | width: 1%; 227 | white-space: nowrap; 228 | padding-right: 10px; 229 | } 230 | 231 | td.middle { 232 | text-align:center; 233 | } 234 | 235 | td.section { 236 | text-align:center; 237 | background-color:var(--blockquote-background); 238 | } 239 | 240 | pre { 241 | padding: 10px; 242 | background: var(--code-background); 243 | color: var(--code-foreground); 244 | border-radius: var(--border-radius-small); 245 | } 246 | 247 | /******** Custom tags (explanation section) *****/ 248 | dl.tag dt { 249 | float: left; 250 | background-color: black; 251 | color: white; 252 | line-height: 1.3em; 253 | padding: 0 5px; 254 | } 255 | 256 | dl.tag blockquote { 257 | border-left: 4px solid #9CAFD4; 258 | } 259 | 260 | dl.item dt { 261 | font-size: 110%; 262 | font-weight: normal; padding: var(--spacing-medium); 263 | background: var(--code-background); 264 | border: 1px solid var(--separator-color); 265 | border-radius: var(--border-radius-medium) var(--border-radius-medium) 0 0; 266 | margin-bottom: -1px; 267 | overflow: auto; 268 | } 269 | dl.item dd { 270 | margin-inline-start: initial; 271 | padding: var(--spacing-medium); 272 | border: 1px solid var(--separator-color); 273 | border-radius: 0 0 var(--border-radius-medium) var(--border-radius-medium); 274 | overflow: auto; 275 | } 276 | 277 | div.label { 278 | font-weight: bold; 279 | } 280 | 281 | div.memdoc p { 282 | margin-top: 0; 283 | } 284 | 285 | dl.item p { 286 | margin-top: 0; 287 | } 288 | 289 | /******** Custom images (center) *****/ 290 | img.inline { 291 | display: block; 292 | margin: auto; 293 | } 294 | 295 | img.right { 296 | float:right; 297 | clear:right; 298 | margin:0 20px 0 20px; 299 | } 300 | 301 | #preview { 302 | position:absolute; 303 | border:1px solid #ccc; 304 | background:#77f; 305 | padding:2px; 306 | display:none; 307 | color:#fff; 308 | } 309 | -------------------------------------------------------------------------------- /spexygen-awesome/spexy-footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /spexygen-awesome/spexy-footer.tex: -------------------------------------------------------------------------------- 1 | % original: https://github.com/doxygen/doxygen/blob/master/templates/latex/footer.tex 2 | % modified by Quantum Leaps on 22-10-27 3 | 4 | %--- End generated contents --- 5 | 6 | %%BEGIN CITATIONS_PRESENT 7 | % Bibliography 8 | \newpage 9 | \phantomsection 10 | 11 | %%BEGIN !PDF_HYPERLINKS 12 | \clearemptydoublepage 13 | %%BEGIN COMPACT_LATEX 14 | \addcontentsline{toc}{section}{$latexcitereference} 15 | %%END COMPACT_LATEX 16 | %%BEGIN !COMPACT_LATEX 17 | \addcontentsline{toc}{chapter}{$latexcitereference} 18 | %%END !COMPACT_LATEX 19 | \printindex 20 | %%END !PDF_HYPERLINKS 21 | 22 | \bibliographystyle{$latexbibstyle} 23 | \bibliography{$latexbibfiles} 24 | %%BEGIN PDF_HYPERLINKS 25 | %%BEGIN COMPACT_LATEX 26 | \addcontentsline{toc}{section}{$latexcitereference} 27 | %%END COMPACT_LATEX 28 | %%BEGIN !COMPACT_LATEX 29 | \addcontentsline{toc}{chapter}{$latexcitereference} 30 | %%END !COMPACT_LATEX 31 | %%END PDF_HYPERLINKS 32 | 33 | %%END CITATIONS_PRESENT 34 | 35 | % Index 36 | %%BEGIN !COMPACT_LATEX 37 | \backmatter 38 | %%END !COMPACT_LATEX 39 | \newpage 40 | \phantomsection 41 | \clearemptydoublepage 42 | %%BEGIN COMPACT_LATEX 43 | \addcontentsline{toc}{section}{\indexname} 44 | %%END COMPACT_LATEX 45 | %%BEGIN !COMPACT_LATEX 46 | \addcontentsline{toc}{chapter}{\indexname} 47 | %%END !COMPACT_LATEX 48 | \printindex 49 | 50 | % Required for some languages (in combination with latexdocumentpre from the header) 51 | $latexdocumentpost 52 | \end{document} 53 | 54 | -------------------------------------------------------------------------------- /spexygen-awesome/spexy-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $projectname: $title 9 | $title 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | $treeview 23 | $search 24 | $mathjax 25 | 26 | $extrastylesheet 27 | 28 | 29 | 30 |
31 | 32 | 33 |
34 | 37 | 38 | 39 |
40 | $projectname 41 |  $projectnumber 42 |
43 |
$projectbrief
44 | 45 | 46 | 47 | $searchbox 48 | 49 | 50 |
51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /spexygen-awesome/spexy-header.tex: -------------------------------------------------------------------------------- 1 | % Latex header for doxygen 1.11.0 2 | % modified by Quantum Leaps on 24-05-17 3 | 4 | % Handle batch mode 5 | $latex_batchmode 6 | 7 | % to overcome problems with too many open files 8 | \let\mypdfximage\pdfximage\def\pdfximage{\immediate\mypdfximage} 9 | 10 | \RequirePackage{iftex} 11 | \ifLuaTeX 12 | \directlua{pdf.setminorversion(7)} 13 | \fi 14 | \ifXeTeX 15 | \special{pdf:minorversion 7} 16 | \fi 17 | \ifPDFTeX 18 | \pdfminorversion=7 19 | \fi 20 | 21 | % Set document class depending on configuration 22 | %%BEGIN COMPACT_LATEX 23 | \documentclass[twoside]{article} 24 | %%END COMPACT_LATEX 25 | %%BEGIN !COMPACT_LATEX 26 | \documentclass[twoside]{book} 27 | %%END !COMPACT_LATEX 28 | 29 | %% moved from doxygen.sty due to workaround for LaTex 2019 version and unmaintained tabu package 30 | \usepackage{ifthen} 31 | \ifx\requestedLaTeXdate\undefined 32 | \usepackage{array} 33 | \else 34 | \usepackage{array}[=2016-10-06] 35 | \fi 36 | %% 37 | \usepackage{geometry} 38 | \usepackage[table]{xcolor} 39 | \usepackage{scrextend} 40 | 41 | % custom colors: 42 | \definecolor{frontbg}{rgb}{0.122, 0.435, 0.698} 43 | \definecolor{frontfg}{rgb}{1, 1, 1} 44 | \definecolor{accent}{rgb}{0.035, 0.411, 0.855} 45 | \definecolor{emph}{rgb}{0.035, 0.411, 0.855} 46 | 47 | % Define your CI: 48 | \newcommand{\copyrightname}{Quantum Leaps, LLC} 49 | \newcommand{\contactdetails}{ 50 | \copyright ~Quantum Leaps, LLC\\ 51 | https://www.state-machine.com\\ 52 | info@state-machine.com\\ 53 | } 54 | 55 | % Packages required by doxygen 56 | \makeatletter 57 | \providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion} 58 | % suppress package identification of infwarerr as it contains the word "warning" 59 | \let\@@protected@wlog\protected@wlog 60 | \def\protected@wlog#1{\wlog{package info suppressed}} 61 | \RequirePackage{infwarerr} 62 | \let\protected@wlog\@@protected@wlog 63 | \makeatother 64 | \IfFormatAtLeastTF{2016/01/01}{}{\usepackage{fixltx2e}} % for \textsubscript 65 | \ifPDFTeX 66 | \IfFormatAtLeastTF{2015/01/01}{\pdfsuppresswarningpagegroup=1}{} 67 | \fi 68 | 69 | \usepackage{doxygen} 70 | 71 | $extralatexstylesheet 72 | 73 | \usepackage{graphicx} 74 | \iftutex 75 | \usepackage{fontspec} 76 | \defaultfontfeatures{Ligatures={TeX}} 77 | \usepackage{unicode-math} 78 | \else 79 | \usepackage[utf8]{inputenc} 80 | \fi 81 | \usepackage{makeidx} 82 | \PassOptionsToPackage{warn}{textcomp} 83 | \usepackage{textcomp} 84 | \usepackage[nointegrals]{wasysym} 85 | \usepackage{ifxetex} 86 | 87 | % NLS support packages 88 | $languagesupport 89 | 90 | % Define default fonts 91 | % Font selection 92 | %%BEGIN LATEX_FONTENC 93 | \iftutex 94 | \else 95 | \usepackage[$latexfontenc]{fontenc} 96 | \fi 97 | %%END LATEX_FONTENC 98 | 99 | % set main and monospaced font 100 | $latexfont 101 | 102 | \doxyallsectionsfont{% 103 | \fontseries{bc}\selectfont% 104 | \color{darkgray}% 105 | } 106 | \renewcommand{\DoxyLabelFont}{% 107 | \fontseries{bc}\selectfont% 108 | \color{darkgray}% 109 | } 110 | 111 | % see: https://stackoverflow.com/questions/15230386/howto-disable-hyphenation-for-typewriter-text-in-doxygen 112 | %\newcommand{\+}{\discretionary{\mbox{\scriptsize$\hookleftarrow$}}{}{}} 113 | \newcommand{\+}{} 114 | 115 | % Arguments of doxygenemoji: 116 | % 1) '::' form of the emoji, already LaTeX-escaped 117 | % 2) file with the name of the emoji without the .png extension 118 | % in case image exist use this otherwise use the '::' form 119 | \newcommand{\doxygenemoji}[2]{% 120 | \IfFileExists{$latexemojidirectory/#2.png}{\raisebox{-0.1em}{\includegraphics[height=0.9em]{$latexemojidirectory/#2.png}}}{#1}% 121 | } 122 | 123 | % Page & text layout 124 | \usepackage{geometry} 125 | \geometry{% 126 | $papertype,% 127 | top=2.5cm,% 128 | bottom=2.5cm,% 129 | left=2.5cm,% 130 | right=2.5cm% 131 | } 132 | \usepackage{changepage} 133 | 134 | % Allow a bit of overflow to go unnoticed by other means 135 | \tolerance=750 136 | \hfuzz=15pt 137 | \hbadness=750 138 | \setlength{\emergencystretch}{15pt} 139 | \setlength{\parindent}{0cm} 140 | \newcommand{\doxynormalparskip}{\setlength{\parskip}{3ex plus 2ex minus 2ex}} 141 | \newcommand{\doxytocparskip}{\setlength{\parskip}{1ex plus 0ex minus 0ex}} 142 | \doxynormalparskip 143 | % Redefine paragraph/subparagraph environments, using sectsty fonts 144 | \makeatletter 145 | \renewcommand{\paragraph}{% 146 | \@startsection{paragraph}{4}{0ex}{-1.0ex}{1.0ex}{% 147 | \normalfont\normalsize\bfseries\SS@parafont% 148 | }% 149 | } 150 | \renewcommand{\subparagraph}{% 151 | \@startsection{subparagraph}{5}{0ex}{-1.0ex}{1.0ex}{% 152 | \normalfont\normalsize\bfseries\SS@subparafont% 153 | }% 154 | } 155 | \makeatother 156 | 157 | \makeatletter 158 | \newcommand\hrulefilll{\leavevmode\leaders\hrule\hskip 0pt plus 1filll\kern\z@} 159 | \makeatother 160 | 161 | % Headers & footers 162 | \usepackage{fancyhdr} 163 | \pagestyle{fancyplain} 164 | \renewcommand{\footrulewidth}{0.4pt} 165 | 166 | \fancypagestyle{fancyplain}{ 167 | \fancyhf{} 168 | \fancyhead[LE, RO]{\bfseries\thepage} 169 | \fancyhead[LO]{\bfseries\rightmark} 170 | \fancyhead[RE]{\bfseries\leftmark} 171 | \fancyfoot[LO, RE]{\bfseries\thepage} 172 | \fancyfoot[C]{\bfseries\scriptsize $projectnumber} 173 | } 174 | 175 | \fancypagestyle{plain}{ 176 | \fancyhf{} 177 | \fancyfoot[LO, RE]{\bfseries\thepage} 178 | \fancyfoot[C]{\bfseries\scriptsize $projectnumber} 179 | \renewcommand{\headrulewidth}{0pt} 180 | } 181 | 182 | \pagestyle{fancyplain} 183 | 184 | 185 | %%BEGIN !COMPACT_LATEX 186 | \renewcommand{\chaptermark}[1]{% 187 | \markboth{#1}{}% 188 | } 189 | %%END !COMPACT_LATEX 190 | \renewcommand{\sectionmark}[1]{% 191 | \markright{\thesection\ #1}% 192 | } 193 | 194 | % ToC, LoF, LoT, bibliography, and index 195 | % Indices & bibliography 196 | \usepackage{natbib} 197 | \usepackage[titles]{tocloft} 198 | \setcounter{tocdepth}{3} 199 | \setcounter{secnumdepth}{5} 200 | 201 | % creating indexes 202 | $makeindex 203 | 204 | $extralatexpackages 205 | 206 | $latexspecialformulachars 207 | 208 | %%BEGIN FORMULA_MACROFILE 209 | \input{$formulamacrofile} 210 | %%END FORMULA_MACROFILE 211 | 212 | % Hyperlinks 213 | %%BEGIN PDF_HYPERLINKS 214 | % Hyperlinks (required, but should be loaded last) 215 | \ifPDFTeX 216 | \usepackage[pdftex,pagebackref=true]{hyperref} 217 | \else 218 | \ifXeTeX 219 | \usepackage[xetex,pagebackref=true]{hyperref} 220 | \else 221 | \ifLuaTeX 222 | \usepackage[luatex,pagebackref=true]{hyperref} 223 | \else 224 | \usepackage[ps2pdf,pagebackref=true]{hyperref} 225 | \fi 226 | \fi 227 | \fi 228 | \hypersetup{% 229 | colorlinks=true,% 230 | linkcolor=blue,% 231 | citecolor=blue,% 232 | unicode,% 233 | pdftitle={$projectname},% 234 | pdfsubject={$projectnumber}% 235 | } 236 | 237 | %%END PDF_HYPERLINKS 238 | 239 | % Custom commands used by the header 240 | % Custom commands 241 | \newcommand{\clearemptydoublepage}{% 242 | \newpage{\pagestyle{empty}\cleardoublepage}% 243 | } 244 | 245 | % caption style definition 246 | \usepackage{caption} 247 | \captionsetup{labelsep=space,justification=centering,font={bf},singlelinecheck=off,skip=4pt,position=top} 248 | 249 | 250 | % in page table of contents 251 | \IfFormatAtLeastTF{2023/05/01}{\usepackage[deeplevels]{etoc}}{\usepackage[deeplevels]{etoc_doxygen}} 252 | \etocsettocstyle{\doxytocparskip}{\doxynormalparskip} 253 | \etocsetlevel{subsubsubsection}{4} 254 | \etocsetlevel{subsubsubsubsection}{5} 255 | \etocsetlevel{subsubsubsubsubsection}{6} 256 | \etocsetlevel{subsubsubsubsubsubsection}{7} 257 | \etocsetlevel{paragraph}{8} 258 | \etocsetlevel{subparagraph}{9} 259 | 260 | % prevent numbers overlap the titles in toc 261 | \renewcommand{\numberline}[1]{#1~} 262 | 263 | % End of preamble, now comes the document contents 264 | %===== C O N T E N T S ===== 265 | 266 | \begin{document} 267 | \raggedbottom 268 | \raggedright 269 | 270 | $latexdocumentpre 271 | 272 | % Titlepage & ToC 273 | %%BEGIN PDF_HYPERLINKS 274 | %%BEGIN USE_PDFLATEX 275 | % To avoid duplicate page anchors due to reuse of same numbers for 276 | % the index (be it as roman numbers) 277 | \hypersetup{pageanchor=false, 278 | bookmarksnumbered=true, 279 | pdfencoding=unicode 280 | } 281 | %%END USE_PDFLATEX 282 | %%END PDF_HYPERLINKS 283 | \pagenumbering{alph} 284 | \begin{titlepage} 285 | \pagecolor{frontbg} 286 | \color{frontfg} 287 | \vspace*{5cm} 288 | {\fontsize{40}{40} \selectfont $projectname\\} 289 | \vspace*{1cm} 290 | {\fontsize{30}{30} \selectfont $projectbrief\\} 291 | 292 | \line(1,0){800}\\ 293 | \vspace*{0.3cm} 294 | {\fontsize{20}{20} \selectfont $projectnumber\\} 295 | %%BEGIN LATEX_TIMESTAMP 296 | %%END LATEX_TIMESTAMP 297 | \vspace*{7cm} 298 | \begin{addmargin}[30em]{0em} 299 | \fontsize{12}{14} \selectfont \contactdetails 300 | \end{addmargin} 301 | \end{titlepage} 302 | 303 | \pagecolor{white} 304 | \color{black} 305 | %%BEGIN !COMPACT_LATEX 306 | \clearemptydoublepage 307 | %%END !COMPACT_LATEX 308 | \pagenumbering{roman} 309 | 310 | \tableofcontents 311 | %%BEGIN !COMPACT_LATEX 312 | \clearemptydoublepage 313 | %%END !COMPACT_LATEX 314 | \pagenumbering{arabic} 315 | 316 | %%BEGIN PDF_HYPERLINKS 317 | %%BEGIN USE_PDFLATEX 318 | % re-enable anchors again 319 | \hypersetup{pageanchor=true} 320 | %%END USE_PDFLATEX 321 | %%END PDF_HYPERLINKS 322 | 323 | %--- Begin generated contents --- 324 | 325 | -------------------------------------------------------------------------------- /spexygen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #============================================================================= 4 | # _Spexygen_ - Traceable Specifications Based on doxygen 5 | # Copyright (C) 2024 Quantum Leaps, LLC 6 | # 7 | # SPDX-License-Identifier: MIT 8 | # 9 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | # this software and associated documentation files (the "Software"), to deal in 11 | # the Software without restriction, including without limitation the rights to 12 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 13 | # the Software, and to permit persons to whom the Software is furnished to do so, 14 | # subject to the following conditions: 15 | # 16 | # The above copyright notice and this permission notice shall be included in all 17 | # copies or substantial portions of the Software. 18 | # 19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 21 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 22 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 23 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | # 26 | # Contact information: 27 | # 28 | # 29 | #============================================================================= 30 | 31 | ''' 32 | Spexygen is a Doxygen extension for creating traceable technical 33 | specifications, such as: 34 | - traceable requirement specifications 35 | - traceable source code 36 | - traceable tests 37 | - traceable specifications of other kind 38 | ''' 39 | 40 | import sys 41 | import os 42 | import json 43 | 44 | class Spexygen: 45 | ''' 46 | Spexygen class that encapsulates tracing and code generation 47 | ''' 48 | 49 | # public class constants 50 | VERSION = 225 51 | 52 | UID_DOC = 1 53 | UID_CODE = 2 54 | LEVELS = ('', ' ', ' ', ' ', ' ') 55 | _debug = False 56 | 57 | def __init__(self): 58 | # private members 59 | self._uid = None # current UID 60 | self._kind = 0 # current UID-kind 61 | self._fname = '' # current file name (for generating output) 62 | self._lnum = 0 # current linie number (for generating output) 63 | self._file = None # current file (for generating output) 64 | self._prefix = '' # prefix for each generated line 65 | self._bw_trace = '' # bw-trace for current uid requested 66 | self._uid_trace_dict = {} # UID dictionary collected during tracing 67 | self._uid_brief_dict = {} # UID-brief dictionary collected during tracing 68 | self._uid_traced_list = [] # UID list built during generation 69 | self._fw_trace_levels = len(Spexygen.LEVELS) - 2 # number of fw-trace levels to generate 70 | 71 | @staticmethod 72 | def debug(*args, **kwargs): 73 | '''debug output (if requesed in command-line parameters) 74 | ''' 75 | if Spexygen._debug: 76 | print(*args, **kwargs) 77 | 78 | 79 | def on_file_pattern(self, fname): 80 | '''return True if file-name is recognizable to Spexygen 81 | ''' 82 | return fname.endswith('.dox') or \ 83 | fname.endswith('.h') or fname.endswith('.c') or \ 84 | fname.endswith('.hpp') or fname.endswith('.cpp') or \ 85 | fname.endswith('.py') or fname.endswith('.lnt') 86 | 87 | def on_gen_fw_trace(self, uid_in, level): 88 | '''recursively generate the forward trace for a "uid" 89 | ''' 90 | self._uid_traced_list.append(uid_in) 91 | Spexygen.debug(" level=", level, "uid=", uid_in, 92 | "self._uid_traced_list:", self._uid_traced_list) 93 | for uid in self._uid_trace_dict.get(uid_in): 94 | if uid not in self._uid_traced_list: 95 | self._file.write("%s%s- @tr{%s}: %s\n" 96 | %(self._prefix, Spexygen.LEVELS[level], 97 | uid, self._uid_brief_dict[uid])) 98 | if level < self._fw_trace_levels: 99 | if uid in self._uid_trace_dict: 100 | self.on_gen_fw_trace(uid, level+1) # recursive! 101 | elif level >= len(Spexygen.LEVELS) - 2: 102 | #self._file.write("%s%s- ...\n" 103 | # %(self._prefix, Spexygen.LEVELS[level+1])) 104 | self._file.write( 105 | f"{self._prefix}{Spexygen.LEVELS[level+1]}- ...\n") 106 | print(f" {self._fname}:{self._lnum} too many"\ 107 | f' forward trace levels for "{uid}"') 108 | 109 | def uid_begin(self, line): 110 | '''set the current uid 'self._uid' and 'self._kind' 111 | if UID found in the given 'line' 112 | ''' 113 | if self._uid != '': 114 | print("Looking for new UID while previous is still active"\ 115 | f" {self._uid}") 116 | kind = Spexygen.UID_DOC 117 | if (i := line.find('@uid{')) >= 0: 118 | l = 5 119 | elif (i := line.find('@code_uid{')) >= 0: 120 | kind = Spexygen.UID_CODE 121 | l = 10 122 | elif (i := line.find('@code_alias{')) >= 0: 123 | kind = Spexygen.UID_CODE 124 | l = 12 125 | else: 126 | return 127 | 128 | j = line.find(',', i + l) 129 | if j < 0: 130 | print("Error: missing ',' in UID definition", 131 | "line", self._lnum, ":", i) 132 | return 133 | 134 | k = line.find('}', j + 1) 135 | if k < 0: 136 | print("Error: missing '}' in UID definition", 137 | "line", self._lnum, ":", i) 138 | return 139 | 140 | self._kind = kind 141 | self._uid = line[i + l:j].strip() 142 | brief = line[j + 1:k].strip() 143 | self._bw_trace = '' 144 | if self._uid not in self._uid_brief_dict: 145 | self._uid_brief_dict[self._uid] = brief 146 | Spexygen.debug(" uid:", self._uid, brief) 147 | 148 | def uid_end(self, line): 149 | '''return True if UID-end was found in the "line" 150 | ''' 151 | if self._uid == '': 152 | print("Looking for UID-end while no UID active") 153 | return False 154 | 155 | if self._kind == Spexygen.UID_DOC: 156 | if line.find('@enduid') >= 0: 157 | Spexygen.debug(" end:", self._uid) 158 | self._uid = '' 159 | self._bw_trace = '' 160 | return True 161 | elif self._kind == Spexygen.UID_CODE: 162 | if line.find('@endcode_uid') >= 0: 163 | Spexygen.debug(" end:", self._uid) 164 | self._uid = '' 165 | self._bw_trace = '' 166 | return True 167 | else: 168 | print(f"Unknown current UID kind={self._kind}") 169 | 170 | return False 171 | 172 | def uid_bw_trace(self, line): 173 | '''return True if bw-trace placeholder found in "line" 174 | ''' 175 | if self._uid == '': 176 | return False 177 | l = 0 178 | i = -1 179 | if self._kind == Spexygen.UID_DOC: 180 | if (i := line.find('@uid_bw_trace')) >= 0: 181 | l = 13 182 | elif self._kind == Spexygen.UID_CODE: 183 | if (i := line.find('@code_bw_trace')) >= 0: 184 | l = 14 185 | else: 186 | print(f"Unknown current UID kind={self._kind}") 187 | return False 188 | 189 | if i < 0: 190 | return False 191 | 192 | if line.find('{', i+l) == i+l: # parameter present? 193 | j = line.find('}', i+l+1) 194 | if j >= 0: 195 | self._bw_trace = line[i+l+1:j] 196 | else: 197 | print("Error: missing '}' for '@uid_bw_trace{' in line", 198 | self._lnum, ":", i+l+1) 199 | return False 200 | else: 201 | self._bw_trace = 'empty' 202 | return True 203 | 204 | def uid_tr(self, line): 205 | '''return list of backward traces found in a given "line" 206 | ''' 207 | tr_list = [] 208 | i = 0 209 | while (i := line.find('@tr{', i)) >= 0: 210 | j = line.find('}', i + 4) 211 | if j >= 0: 212 | tr = line[i + 4:j] 213 | tr_list.append(tr) 214 | i = j 215 | else: 216 | print("Error: missing '}' for '@tr{' in line", 217 | self._lnum, ":", i) 218 | break 219 | return tr_list 220 | 221 | def trace(self, fname): 222 | '''trace a given file and harvest the traces into the 223 | dictionaries: self._uid_brief_dict and self._uid_trace_dict 224 | ''' 225 | try: 226 | f = open(fname, encoding="utf-8") 227 | except OSError: 228 | print("File not found", fname) 229 | return 230 | with f: 231 | lines = f.readlines() 232 | 233 | print("Tracing:", fname) 234 | self._uid = '' 235 | self._lnum = 0 236 | for line in lines: 237 | self._lnum += 1 238 | if self._uid == '': 239 | self.uid_begin(line) 240 | else: 241 | if self.uid_end(line): 242 | pass 243 | elif self.uid_bw_trace(line): 244 | pass 245 | elif self._bw_trace != '': 246 | tr_list = self.uid_tr(line) 247 | for tr in tr_list: 248 | if not self._uid_trace_dict.get(tr): 249 | self._uid_trace_dict[tr] = [] 250 | if self._uid not in self._uid_trace_dict[tr]: 251 | self._uid_trace_dict[tr].append(self._uid) 252 | Spexygen.debug(tr, '<-', self._uid) 253 | 254 | def gen_bw_trace(self, line): 255 | '''find a bw-trace placeholder 256 | as long as bw-trace found 257 | return True if bw-trace placeholder or "@tr{}" found 258 | ''' 259 | if self.uid_bw_trace(line): 260 | self._file.write(line) 261 | return True 262 | 263 | if self._bw_trace != '': 264 | if (i := line.find('@tr{')) >= 0: 265 | j = line.find('}', i + 4) 266 | tr = '' 267 | if j >= 0: 268 | tr = line[i + 4:j] 269 | else: 270 | print("Error: missing '}' for '@tr{' in line", 271 | self._lnum, ":", i) 272 | self._file.write(line) 273 | return True 274 | if tr in self._uid_brief_dict: 275 | if self._bw_trace == 'brief': 276 | self._file.write(line[:j+1]) 277 | self._file.write(f": {self._uid_brief_dict[tr]}") 278 | self._file.write(line[j+1:]) 279 | else: 280 | self._file.write(line) 281 | else: 282 | print(f' {self._fname}:{self._lnum} '\ 283 | f'"{tr}" undefined in backward trace'\ 284 | f' for UID: "{self._uid}"') 285 | self._file.write(line) 286 | return True 287 | return False 288 | 289 | 290 | def gen_fw_trace(self, line): 291 | '''find a fw-trace placeholder and generate fw-trace 292 | return True if the placeholder found 293 | ''' 294 | l = 0 295 | if (i := line.find('@uid_fw_trace')) >= 0: 296 | l = 13 297 | elif (i := line.find('@code_fw_trace')) >= 0: 298 | l = 14 299 | if i < 0: 300 | return False # placeholder not found 301 | 302 | # parse the optional number of levels parameter 303 | levels = len(Spexygen.LEVELS) - 2 304 | if line.find('{', i+l) == i+l: # parameter present? 305 | j = line.find('}', i+l+1) 306 | if j >= 0: 307 | try: 308 | levels = int(line[i+l+1:j]) 309 | if levels >= 1: 310 | levels -= 1 311 | elif levels > len(Spexygen.LEVELS) - 2: 312 | levels = len(Spexygen.LEVELS) - 2 313 | except ValueError: 314 | print("Error: non-numeric parameter in '@uid_fw_trace{}'") 315 | return False 316 | else: 317 | print("Error: missing '}' for '@uid_fw_trace{' in line", 318 | self._lnum, ":", i+l+1) 319 | return False 320 | self._fw_trace_levels = levels 321 | 322 | self._prefix = line[:i] 323 | self._file.write(line) 324 | if self._uid in self._uid_trace_dict: 325 | self._uid_traced_list = [] 326 | self.on_gen_fw_trace(self._uid, 0) 327 | else: 328 | print(f' {self._fname}:{self._lnum} empty forward trace'\ 329 | f' for UID: "{self._uid}"') 330 | 331 | return True 332 | 333 | def gen(self, gendir, fname): 334 | '''generate a given file, replacing the detected placeholdrs 335 | with information from the dictionaries: 336 | self._uid_brief_dict and self._uid_trace_dict 337 | ''' 338 | try: 339 | f = open(fname, encoding="utf-8") 340 | except OSError: 341 | print("File not found", fname) 342 | return 343 | with f: 344 | lines = f.readlines() 345 | 346 | self._fname = os.path.basename(fname) 347 | fname = gendir + '/' + self._fname 348 | try: 349 | self._file = open(fname, 'w', encoding="utf-8") 350 | except OSError: 351 | print("File cannot be written", fname) 352 | return 353 | with self._file: 354 | print("Generating:", fname) 355 | self._lnum = 0 356 | self._uid = '' 357 | self._bw_trace = '' 358 | for line in lines: 359 | self._lnum += 1 360 | if self._uid == '': # no current item 361 | self._file.write(line) 362 | self.uid_begin(line) 363 | else: # have current item 364 | if self.uid_end(line): 365 | self._file.write(line) 366 | elif self.gen_fw_trace(line): 367 | pass 368 | elif self.gen_bw_trace(line): 369 | pass 370 | else: 371 | self._file.write(line) 372 | 373 | def main(self): 374 | '''main entry point to Spexygen 375 | process command-line arguments and run Spexygen 376 | ''' 377 | print(f"Spexygen: traceable technical documentation system "\ 378 | f"{self.VERSION//100}.{(self.VERSION//10) % 10}."\ 379 | f"{self.VERSION % 10}") 380 | print("Copyright (c) 2005-2024 Quantum Leaps, www.state-machine.com\n") 381 | 382 | # defaults 383 | sfname = 'spex.json' 384 | gendir = 'spex' 385 | spexyinp = 'Spexyinput' 386 | 387 | # process command-line arguments... 388 | argv = sys.argv 389 | argc = len(argv) 390 | arg = 1 # skip the 'Spexygen' argument 391 | 392 | if '-h' in argv or '--help' in argv or '?' in argv \ 393 | or '--version' in argv: 394 | print("Usage: python [-d] spexygen.py [spexyfile]", 395 | "\n(deafult spexyfile: 'spexy.json')") 396 | sys.exit(0) 397 | 398 | if '-d' in argv: 399 | Spexygen._debug = True 400 | argv.remove('-d') 401 | argc -= 1 402 | 403 | if arg < argc: 404 | # is the next argument a test script? 405 | sfname = argv[arg] 406 | 407 | # parse the provided spexyfile as json data 408 | try: 409 | sfile = open(sfname, encoding="utf-8") 410 | except OSError: 411 | print("spexyfile not found:", sfname) 412 | sys.exit(-1) 413 | with sfile: 414 | try: 415 | spex = json.load(sfile) 416 | except ValueError as e: 417 | print("Error in spexyfile:", sfname) 418 | print(e) 419 | sys.exit(-1) 420 | 421 | # perform tracing 422 | key = 'trace' 423 | if key not in spex: 424 | key = 'gen' # no 'trace' section means use 'gen' section 425 | if key not in spex: 426 | print("Spexygen: nothing to do") 427 | return 428 | for path in spex[key]: 429 | if os.path.isfile(path): 430 | if self.on_file_pattern(path): 431 | self.trace(path) 432 | elif os.path.isdir(path): 433 | for fname in os.listdir(path): 434 | if self.on_file_pattern(fname): 435 | self.trace(path + '/' + fname) 436 | else: 437 | print("not exist:", path) 438 | Spexygen.debug("\n_uid_trace_dict", self._uid_trace_dict) 439 | Spexygen.debug("\n_uid_brief_dict", self._uid_brief_dict) 440 | 441 | key = 'gen-dir' 442 | if key in spex: 443 | gendir = spex[key] 444 | if gendir == '': 445 | print("No code generation (empty gen directory)") 446 | return 447 | 448 | key = 'gen-inc' 449 | if key in spex: 450 | spexyinp = spex[key] 451 | 452 | key = 'gen' 453 | geninc = None 454 | if key not in spex: 455 | print("Spexygen: nothing to generate") 456 | return 457 | 458 | if not os.path.exists(gendir): 459 | os.mkdir(gendir) 460 | if spexyinp != '': 461 | try: 462 | geninc = open(gendir + '/' + spexyinp, 'w', 463 | encoding="utf-8") 464 | except OSError: 465 | print("spexyinp cannot be written:", spexyinp) 466 | sys.exit(-1) 467 | if geninc: 468 | geninc.write('INPUT +=') 469 | 470 | # generate documentation 471 | for path in spex[key]: 472 | if os.path.isfile(path): 473 | if self.on_file_pattern(path): 474 | self.gen(gendir, path) 475 | if geninc: 476 | geninc.write(' \\\n' + gendir + '/' 477 | + os.path.basename(path)) 478 | elif os.path.isdir(path): 479 | for fname in os.listdir(path): 480 | if self.on_file_pattern(fname): 481 | self.gen(gendir, path + '/' + fname) 482 | if geninc: 483 | geninc.write(' \\\n' + gendir + '/' 484 | + os.path.basename(fname)) 485 | else: 486 | print("not exist:", path) 487 | 488 | if geninc: 489 | geninc.close() 490 | 491 | #============================================================================= 492 | if __name__ == "__main__": 493 | spx = Spexygen() 494 | spx.main() 495 | --------------------------------------------------------------------------------