wireforge 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wireforge-0.1.0/LICENSE +98 -0
- wireforge-0.1.0/MANIFEST.in +8 -0
- wireforge-0.1.0/PKG-INFO +91 -0
- wireforge-0.1.0/README.md +51 -0
- wireforge-0.1.0/cmake/package/WireForgeConfig.cmake +29 -0
- wireforge-0.1.0/cmake/package/WireForgeConfigVersion.cmake +15 -0
- wireforge-0.1.0/cmake/package/WireForgeGenerate.cmake +64 -0
- wireforge-0.1.0/codegen/wireforge/__init__.py +3 -0
- wireforge-0.1.0/codegen/wireforge/bundle.py +177 -0
- wireforge-0.1.0/codegen/wireforge/ci_check.py +155 -0
- wireforge-0.1.0/codegen/wireforge/cli.py +617 -0
- wireforge-0.1.0/codegen/wireforge/codec.py +897 -0
- wireforge-0.1.0/codegen/wireforge/compat.py +536 -0
- wireforge-0.1.0/codegen/wireforge/coverage_points.py +64 -0
- wireforge-0.1.0/codegen/wireforge/cpp.py +273 -0
- wireforge-0.1.0/codegen/wireforge/cpp_codec.py +440 -0
- wireforge-0.1.0/codegen/wireforge/cpp_interface.py +874 -0
- wireforge-0.1.0/codegen/wireforge/cpp_tests.py +1993 -0
- wireforge-0.1.0/codegen/wireforge/differential.py +586 -0
- wireforge-0.1.0/codegen/wireforge/docs_markdown.py +72 -0
- wireforge-0.1.0/codegen/wireforge/expr.py +598 -0
- wireforge-0.1.0/codegen/wireforge/findings.py +187 -0
- wireforge-0.1.0/codegen/wireforge/fmt.py +351 -0
- wireforge-0.1.0/codegen/wireforge/generate.py +644 -0
- wireforge-0.1.0/codegen/wireforge/golden.py +253 -0
- wireforge-0.1.0/codegen/wireforge/hlr_import.py +112 -0
- wireforge-0.1.0/codegen/wireforge/impact.py +249 -0
- wireforge-0.1.0/codegen/wireforge/importers.py +402 -0
- wireforge-0.1.0/codegen/wireforge/issues.py +27 -0
- wireforge-0.1.0/codegen/wireforge/layout.py +681 -0
- wireforge-0.1.0/codegen/wireforge/lifecycle.py +1266 -0
- wireforge-0.1.0/codegen/wireforge/lifecycle_results.py +205 -0
- wireforge-0.1.0/codegen/wireforge/lifecycle_rules.py +78 -0
- wireforge-0.1.0/codegen/wireforge/lifecycle_runtime.py +375 -0
- wireforge-0.1.0/codegen/wireforge/llr.py +693 -0
- wireforge-0.1.0/codegen/wireforge/llr_ids.py +104 -0
- wireforge-0.1.0/codegen/wireforge/loader.py +422 -0
- wireforge-0.1.0/codegen/wireforge/mcp_server.py +357 -0
- wireforge-0.1.0/codegen/wireforge/model.py +322 -0
- wireforge-0.1.0/codegen/wireforge/plain.py +15 -0
- wireforge-0.1.0/codegen/wireforge/py.typed +0 -0
- wireforge-0.1.0/codegen/wireforge/reqif.py +289 -0
- wireforge-0.1.0/codegen/wireforge/resources.py +43 -0
- wireforge-0.1.0/codegen/wireforge/results.py +377 -0
- wireforge-0.1.0/codegen/wireforge/runtime_reqs.py +147 -0
- wireforge-0.1.0/codegen/wireforge/schema.py +151 -0
- wireforge-0.1.0/codegen/wireforge/server/__init__.py +1 -0
- wireforge-0.1.0/codegen/wireforge/server/app.py +619 -0
- wireforge-0.1.0/codegen/wireforge/server/oidc.py +99 -0
- wireforge-0.1.0/codegen/wireforge/server/storage.py +146 -0
- wireforge-0.1.0/codegen/wireforge/service.py +1354 -0
- wireforge-0.1.0/codegen/wireforge/templates/command/project.yaml +38 -0
- wireforge-0.1.0/codegen/wireforge/templates/raw/project.yaml +28 -0
- wireforge-0.1.0/codegen/wireforge/templates/telemetry/project.yaml +51 -0
- wireforge-0.1.0/codegen/wireforge/test_cases.py +475 -0
- wireforge-0.1.0/codegen/wireforge/test_project.py +478 -0
- wireforge-0.1.0/codegen/wireforge/trace.py +442 -0
- wireforge-0.1.0/codegen/wireforge/validate.py +1241 -0
- wireforge-0.1.0/codegen/wireforge/vcs.py +44 -0
- wireforge-0.1.0/codegen/wireforge/vectors.py +222 -0
- wireforge-0.1.0/codegen/wireforge/versioning.py +137 -0
- wireforge-0.1.0/codegen/wireforge/wireshark.py +792 -0
- wireforge-0.1.0/codegen/wireforge/wireshark_runtime.lua +725 -0
- wireforge-0.1.0/codegen/wireforge.egg-info/PKG-INFO +91 -0
- wireforge-0.1.0/codegen/wireforge.egg-info/SOURCES.txt +100 -0
- wireforge-0.1.0/codegen/wireforge.egg-info/dependency_links.txt +1 -0
- wireforge-0.1.0/codegen/wireforge.egg-info/entry_points.txt +2 -0
- wireforge-0.1.0/codegen/wireforge.egg-info/requires.txt +33 -0
- wireforge-0.1.0/codegen/wireforge.egg-info/top_level.txt +1 -0
- wireforge-0.1.0/pyproject.toml +79 -0
- wireforge-0.1.0/runtime/LICENSE +21 -0
- wireforge-0.1.0/runtime/include/wireforge/array_access.hpp +35 -0
- wireforge-0.1.0/runtime/include/wireforge/bit_utils.hpp +226 -0
- wireforge-0.1.0/runtime/include/wireforge/byte_io.hpp +387 -0
- wireforge-0.1.0/runtime/include/wireforge/checksum.hpp +201 -0
- wireforge-0.1.0/runtime/include/wireforge/codec_support.hpp +412 -0
- wireforge-0.1.0/runtime/include/wireforge/config.hpp +41 -0
- wireforge-0.1.0/runtime/include/wireforge/delegate.hpp +101 -0
- wireforge-0.1.0/runtime/include/wireforge/dispatcher.hpp +339 -0
- wireforge-0.1.0/runtime/include/wireforge/expr_support.hpp +38 -0
- wireforge-0.1.0/runtime/include/wireforge/frame_decoder.hpp +262 -0
- wireforge-0.1.0/runtime/include/wireforge/frame_layout.hpp +20 -0
- wireforge-0.1.0/runtime/include/wireforge/link.hpp +223 -0
- wireforge-0.1.0/runtime/include/wireforge/lock.hpp +146 -0
- wireforge-0.1.0/runtime/include/wireforge/ring_buffer.hpp +226 -0
- wireforge-0.1.0/runtime/include/wireforge/span.hpp +116 -0
- wireforge-0.1.0/runtime/include/wireforge/static_vector.hpp +264 -0
- wireforge-0.1.0/runtime/include/wireforge/status.hpp +75 -0
- wireforge-0.1.0/runtime/include/wireforge/transport.hpp +95 -0
- wireforge-0.1.0/runtime/include/wireforge/validation.hpp +153 -0
- wireforge-0.1.0/runtime/include/wireforge/version.hpp +22 -0
- wireforge-0.1.0/runtime/requirements.yaml +438 -0
- wireforge-0.1.0/schema/deviations.schema.json +47 -0
- wireforge-0.1.0/schema/fragment.schema.json +79 -0
- wireforge-0.1.0/schema/llr_ids.schema.json +25 -0
- wireforge-0.1.0/schema/project.schema.json +1240 -0
- wireforge-0.1.0/schema/runtime_requirements.schema.json +34 -0
- wireforge-0.1.0/setup.cfg +4 -0
- wireforge-0.1.0/setup.py +45 -0
- wireforge-0.1.0/ui/dist/assets/index-6Pomu6U4.js +44 -0
- wireforge-0.1.0/ui/dist/assets/index-GVdDyUAG.css +1 -0
- wireforge-0.1.0/ui/dist/index.html +13 -0
wireforge-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: Oguzhan KAYNAK
|
|
6
|
+
Licensed Work: WireForge (this repository), excluding the contents of the
|
|
7
|
+
`runtime/` directory, which are separately licensed under
|
|
8
|
+
the MIT License (see runtime/LICENSE). The Licensed Work
|
|
9
|
+
is (c) 2026 Oguzhan KAYNAK.
|
|
10
|
+
Additional Use Grant: You may use the Licensed Work for development, testing,
|
|
11
|
+
evaluation, and other non-production purposes free of
|
|
12
|
+
charge. Any production use of the Licensed Work requires
|
|
13
|
+
a commercial license from the Licensor.
|
|
14
|
+
Change Date: 2036-10-17
|
|
15
|
+
Change License: MIT
|
|
16
|
+
|
|
17
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
18
|
+
please contact the Licensor.
|
|
19
|
+
|
|
20
|
+
Notice
|
|
21
|
+
|
|
22
|
+
The Business Source License (this document, or the "License") is not an Open
|
|
23
|
+
Source license. However, the Licensed Work will eventually be made available
|
|
24
|
+
under an Open Source License, as stated in this License.
|
|
25
|
+
|
|
26
|
+
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
|
|
27
|
+
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
|
28
|
+
|
|
29
|
+
-----------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
Business Source License 1.1
|
|
32
|
+
|
|
33
|
+
Terms
|
|
34
|
+
|
|
35
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
36
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
37
|
+
Licensor may make an Additional Use Grant, above, permitting limited
|
|
38
|
+
production use.
|
|
39
|
+
|
|
40
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
41
|
+
available distribution of a specific version of the Licensed Work under this
|
|
42
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
43
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
44
|
+
above terminate.
|
|
45
|
+
|
|
46
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
47
|
+
currently in effect as described in this License, you must purchase a
|
|
48
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
49
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
50
|
+
|
|
51
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
52
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
53
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
54
|
+
for each version of the Licensed Work released by Licensor.
|
|
55
|
+
|
|
56
|
+
You must conspicuously display this License on each original or modified copy
|
|
57
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
58
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
59
|
+
License apply to your use of that work.
|
|
60
|
+
|
|
61
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
62
|
+
terminate your rights under this License for the current and all other
|
|
63
|
+
versions of the Licensed Work.
|
|
64
|
+
|
|
65
|
+
This License does not grant you any right in any trademark or logo of
|
|
66
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
67
|
+
Licensor as expressly required by this License).
|
|
68
|
+
|
|
69
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
70
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
71
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
72
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
73
|
+
TITLE.
|
|
74
|
+
|
|
75
|
+
MariaDB hereby grants you permission to use this License's text to license
|
|
76
|
+
your works, and to refer to it using the trademark "Business Source License",
|
|
77
|
+
as long as you comply with the Covenants of Licensor below.
|
|
78
|
+
|
|
79
|
+
Covenants of Licensor
|
|
80
|
+
|
|
81
|
+
In consideration of the right to use this License's text and the "Business
|
|
82
|
+
Source License" name and trademark, Licensor covenants to MariaDB, and to all
|
|
83
|
+
other recipients of the licensed work to be provided by Licensor:
|
|
84
|
+
|
|
85
|
+
1. To specify as the Change License the GPL Version 2.0 or any later version,
|
|
86
|
+
or a license that is compatible with GPL Version 2.0 or a later version,
|
|
87
|
+
where "compatible" means that software provided under the Change License
|
|
88
|
+
can be included in a program with software provided under GPL Version 2.0
|
|
89
|
+
or a later version. Licensor may specify additional Change Licenses
|
|
90
|
+
without limitation.
|
|
91
|
+
|
|
92
|
+
2. To either: (a) specify an additional grant of rights to use that does not
|
|
93
|
+
impose any additional restriction on the right granted in this License, as
|
|
94
|
+
the Additional Use Grant; or (b) insert the text "None".
|
|
95
|
+
|
|
96
|
+
3. To specify a Change Date.
|
|
97
|
+
|
|
98
|
+
4. Not to modify this License in any other way.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# What the source distribution carries so that a wheel can be built from it (setup.py copies these
|
|
2
|
+
# directories into the wheel).
|
|
3
|
+
include LICENSE README.md
|
|
4
|
+
include runtime/LICENSE runtime/requirements.yaml
|
|
5
|
+
recursive-include schema *.json
|
|
6
|
+
recursive-include runtime/include *.hpp
|
|
7
|
+
recursive-include cmake/package *
|
|
8
|
+
recursive-include ui/dist *
|
wireforge-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wireforge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Schema-driven wire-format toolkit: C++17 code generation, validation, server and MCP.
|
|
5
|
+
Author: Oguzhan KAYNAK
|
|
6
|
+
License-Expression: BUSL-1.1 AND MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
License-File: runtime/LICENSE
|
|
11
|
+
Requires-Dist: ruamel.yaml>=0.18
|
|
12
|
+
Requires-Dist: jsonschema>=4.21
|
|
13
|
+
Requires-Dist: referencing>=0.30
|
|
14
|
+
Requires-Dist: typing_extensions>=4.4
|
|
15
|
+
Provides-Extra: server
|
|
16
|
+
Requires-Dist: fastapi>=0.110; extra == "server"
|
|
17
|
+
Requires-Dist: uvicorn>=0.29; extra == "server"
|
|
18
|
+
Provides-Extra: oidc
|
|
19
|
+
Requires-Dist: joserfc>=1.0; extra == "oidc"
|
|
20
|
+
Provides-Extra: mcp
|
|
21
|
+
Requires-Dist: mcp>=2.3; extra == "mcp"
|
|
22
|
+
Provides-Extra: import
|
|
23
|
+
Requires-Dist: openpyxl>=3.1; extra == "import"
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff<0.18,>=0.17; extra == "dev"
|
|
27
|
+
Requires-Dist: mypy<3,>=2.4; extra == "dev"
|
|
28
|
+
Requires-Dist: types-jsonschema; extra == "dev"
|
|
29
|
+
Requires-Dist: gcovr>=7; extra == "dev"
|
|
30
|
+
Requires-Dist: clang-tidy==18.1.8; extra == "dev"
|
|
31
|
+
Requires-Dist: fastapi>=0.110; extra == "dev"
|
|
32
|
+
Requires-Dist: uvicorn>=0.29; extra == "dev"
|
|
33
|
+
Requires-Dist: httpx2>=2.13; extra == "dev"
|
|
34
|
+
Requires-Dist: mcp>=2.3; extra == "dev"
|
|
35
|
+
Requires-Dist: openpyxl>=3.1; extra == "dev"
|
|
36
|
+
Requires-Dist: joserfc>=1.0; extra == "dev"
|
|
37
|
+
Requires-Dist: types-openpyxl; extra == "dev"
|
|
38
|
+
Requires-Dist: pymodbus==3.16.1; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# WireForge
|
|
42
|
+
|
|
43
|
+
Schema-driven wire-format toolkit for C++17: describe binary messages in YAML (by hand, in the web UI, through the CLI
|
|
44
|
+
or an AI agent) and generate allocation-free C++ serializers, validators, framers and dispatchers together with a
|
|
45
|
+
GoogleTest project that proves them against a reference codec and gates the generated code at 100 % line, branch and
|
|
46
|
+
function coverage.
|
|
47
|
+
|
|
48
|
+
- The wire format is imposed by the remote party: generated code never adds bytes.
|
|
49
|
+
- Runtime and generated code use only pre-allocated storage: no heap, no exceptions, no RTTI required.
|
|
50
|
+
- One service layer behind the `wireforge` CLI, the Python API, the REST server with its web UI and the MCP server.
|
|
51
|
+
- A generated Wireshark Lua dissector shows every interface's frames, with the same checks as the decoder.
|
|
52
|
+
- Zero warnings from every compiler and analyzer; tested on 32/64 bit, little and big endian, x86 and ARM, on Linux,
|
|
53
|
+
Windows, macOS, Android and FreeRTOS; differential testing and fuzzing.
|
|
54
|
+
|
|
55
|
+
The way in is short: `wireforge init` gives a working first project, you change it to your protocol, generate the C++
|
|
56
|
+
code, run the tests and read the results. Nothing about requirements or standards is needed for that.
|
|
57
|
+
|
|
58
|
+
## For teams with a development or assurance process
|
|
59
|
+
Optionally, `wireforge docs` turns the same model into requirements, design, test and traceability documents with test
|
|
60
|
+
and coverage results, change impact between revisions and ReqIF/CSV exchange with requirements tools (docs/LIFECYCLE.md,
|
|
61
|
+
docs/integration/). For projects that apply standards such as DO-178C, ISO 26262, IEC 61508 or IEC 62304, this evidence
|
|
62
|
+
supports your processes and your tool assessment; it does not by itself give conformity, certification or tool
|
|
63
|
+
qualification, which depend on the whole project and its assessor (docs/DO178C.md).
|
|
64
|
+
|
|
65
|
+
**Start with docs/GUIDE.md** (install, 5-minute quickstart, examples). Reference:
|
|
66
|
+
|
|
67
|
+
| Topic | Document |
|
|
68
|
+
|-------|----------|
|
|
69
|
+
| Definition format | docs/SCHEMA_SPEC.md, `schema/`, `examples/` |
|
|
70
|
+
| Command line and Python API | docs/CLI.md |
|
|
71
|
+
| Generated C++ and test project | docs/CODEGEN.md, docs/RUNTIME.md |
|
|
72
|
+
| Layout analysis / ICD export | docs/LAYOUT.md |
|
|
73
|
+
| Wireshark dissector (Lua plugin) | docs/WIRESHARK.md |
|
|
74
|
+
| Requirements, design and test documents | docs/LIFECYCLE.md |
|
|
75
|
+
| Fitting into a DO-178C process, tool integration | docs/DO178C.md, docs/integration/ |
|
|
76
|
+
| Server, web UI, MCP | docs/SERVER.md, docs/UI.md, docs/MCP.md |
|
|
77
|
+
| Secure deployment and operation | docs/SERVER.md, "Deployment and operation" |
|
|
78
|
+
| Importers (CSV/Excel ICD, C headers) | docs/IMPORT.md |
|
|
79
|
+
| Quality, safety, traceability | quality/QUALITY_POLICY.md, docs/SAFETY.md, docs/CODING_RULES.md |
|
|
80
|
+
| Architecture and design decisions | docs/ARCHITECTURE.md, docs/DECISIONS.md |
|
|
81
|
+
|
|
82
|
+
Development: see CONTRIBUTING.md (`pip install -e ".[dev]"`, `cmake --preset dev && cmake --build --preset dev && ctest
|
|
83
|
+
--preset dev`, all gates: `quality/run_all.sh`).
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
The `runtime/` library is MIT-licensed (see runtime/LICENSE): code generated by WireForge belongs to you and depends
|
|
87
|
+
only on this MIT-licensed runtime, so shipping it in your product carries no restriction.
|
|
88
|
+
|
|
89
|
+
Everything else (the CLI, code generator, server, web UI, and MCP server) is licensed under the Business Source
|
|
90
|
+
License 1.1 (see LICENSE): free for development, testing, and evaluation; a commercial license is required for
|
|
91
|
+
production use. It converts to MIT on 2036-10-17. Contact the Licensor for commercial licensing.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# WireForge
|
|
2
|
+
|
|
3
|
+
Schema-driven wire-format toolkit for C++17: describe binary messages in YAML (by hand, in the web UI, through the CLI
|
|
4
|
+
or an AI agent) and generate allocation-free C++ serializers, validators, framers and dispatchers together with a
|
|
5
|
+
GoogleTest project that proves them against a reference codec and gates the generated code at 100 % line, branch and
|
|
6
|
+
function coverage.
|
|
7
|
+
|
|
8
|
+
- The wire format is imposed by the remote party: generated code never adds bytes.
|
|
9
|
+
- Runtime and generated code use only pre-allocated storage: no heap, no exceptions, no RTTI required.
|
|
10
|
+
- One service layer behind the `wireforge` CLI, the Python API, the REST server with its web UI and the MCP server.
|
|
11
|
+
- A generated Wireshark Lua dissector shows every interface's frames, with the same checks as the decoder.
|
|
12
|
+
- Zero warnings from every compiler and analyzer; tested on 32/64 bit, little and big endian, x86 and ARM, on Linux,
|
|
13
|
+
Windows, macOS, Android and FreeRTOS; differential testing and fuzzing.
|
|
14
|
+
|
|
15
|
+
The way in is short: `wireforge init` gives a working first project, you change it to your protocol, generate the C++
|
|
16
|
+
code, run the tests and read the results. Nothing about requirements or standards is needed for that.
|
|
17
|
+
|
|
18
|
+
## For teams with a development or assurance process
|
|
19
|
+
Optionally, `wireforge docs` turns the same model into requirements, design, test and traceability documents with test
|
|
20
|
+
and coverage results, change impact between revisions and ReqIF/CSV exchange with requirements tools (docs/LIFECYCLE.md,
|
|
21
|
+
docs/integration/). For projects that apply standards such as DO-178C, ISO 26262, IEC 61508 or IEC 62304, this evidence
|
|
22
|
+
supports your processes and your tool assessment; it does not by itself give conformity, certification or tool
|
|
23
|
+
qualification, which depend on the whole project and its assessor (docs/DO178C.md).
|
|
24
|
+
|
|
25
|
+
**Start with docs/GUIDE.md** (install, 5-minute quickstart, examples). Reference:
|
|
26
|
+
|
|
27
|
+
| Topic | Document |
|
|
28
|
+
|-------|----------|
|
|
29
|
+
| Definition format | docs/SCHEMA_SPEC.md, `schema/`, `examples/` |
|
|
30
|
+
| Command line and Python API | docs/CLI.md |
|
|
31
|
+
| Generated C++ and test project | docs/CODEGEN.md, docs/RUNTIME.md |
|
|
32
|
+
| Layout analysis / ICD export | docs/LAYOUT.md |
|
|
33
|
+
| Wireshark dissector (Lua plugin) | docs/WIRESHARK.md |
|
|
34
|
+
| Requirements, design and test documents | docs/LIFECYCLE.md |
|
|
35
|
+
| Fitting into a DO-178C process, tool integration | docs/DO178C.md, docs/integration/ |
|
|
36
|
+
| Server, web UI, MCP | docs/SERVER.md, docs/UI.md, docs/MCP.md |
|
|
37
|
+
| Secure deployment and operation | docs/SERVER.md, "Deployment and operation" |
|
|
38
|
+
| Importers (CSV/Excel ICD, C headers) | docs/IMPORT.md |
|
|
39
|
+
| Quality, safety, traceability | quality/QUALITY_POLICY.md, docs/SAFETY.md, docs/CODING_RULES.md |
|
|
40
|
+
| Architecture and design decisions | docs/ARCHITECTURE.md, docs/DECISIONS.md |
|
|
41
|
+
|
|
42
|
+
Development: see CONTRIBUTING.md (`pip install -e ".[dev]"`, `cmake --preset dev && cmake --build --preset dev && ctest
|
|
43
|
+
--preset dev`, all gates: `quality/run_all.sh`).
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
The `runtime/` library is MIT-licensed (see runtime/LICENSE): code generated by WireForge belongs to you and depends
|
|
47
|
+
only on this MIT-licensed runtime, so shipping it in your product carries no restriction.
|
|
48
|
+
|
|
49
|
+
Everything else (the CLI, code generator, server, web UI, and MCP server) is licensed under the Business Source
|
|
50
|
+
License 1.1 (see LICENSE): free for development, testing, and evaluation; a commercial license is required for
|
|
51
|
+
production use. It converts to MIT on 2036-10-17. Contact the Licensor for commercial licensing.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# CMake package of WireForge: find_package(WireForge) gives
|
|
2
|
+
# - wireforge::runtime: the header-only C++17 runtime;
|
|
3
|
+
# - wireforge_generate(): generates C++ code from a WireForge project at configure time.
|
|
4
|
+
# Works from an installed tree (<prefix>/lib/cmake/WireForge) and from the Python package
|
|
5
|
+
# (`wireforge cmake-dir` prints this directory).
|
|
6
|
+
include_guard(GLOBAL)
|
|
7
|
+
|
|
8
|
+
set(WIREFORGE_INCLUDE_DIR "")
|
|
9
|
+
foreach(candidate "${CMAKE_CURRENT_LIST_DIR}/../runtime/include" "${CMAKE_CURRENT_LIST_DIR}/../../../include"
|
|
10
|
+
"${CMAKE_CURRENT_LIST_DIR}/../../runtime/include")
|
|
11
|
+
if(NOT WIREFORGE_INCLUDE_DIR AND EXISTS "${candidate}/wireforge/status.hpp")
|
|
12
|
+
get_filename_component(WIREFORGE_INCLUDE_DIR "${candidate}" ABSOLUTE)
|
|
13
|
+
endif()
|
|
14
|
+
endforeach()
|
|
15
|
+
if(NOT WIREFORGE_INCLUDE_DIR)
|
|
16
|
+
set(WireForge_FOUND FALSE)
|
|
17
|
+
set(WireForge_NOT_FOUND_MESSAGE "the WireForge runtime headers were not found next to ${CMAKE_CURRENT_LIST_DIR}")
|
|
18
|
+
return()
|
|
19
|
+
endif()
|
|
20
|
+
|
|
21
|
+
if(NOT TARGET wireforge::runtime)
|
|
22
|
+
add_library(wireforge::runtime INTERFACE IMPORTED)
|
|
23
|
+
set_target_properties(wireforge::runtime PROPERTIES
|
|
24
|
+
INTERFACE_INCLUDE_DIRECTORIES "${WIREFORGE_INCLUDE_DIR}"
|
|
25
|
+
INTERFACE_COMPILE_FEATURES cxx_std_17)
|
|
26
|
+
endif()
|
|
27
|
+
|
|
28
|
+
include("${CMAKE_CURRENT_LIST_DIR}/WireForgeGenerate.cmake")
|
|
29
|
+
set(WireForge_FOUND TRUE)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Version of the WireForge CMake package; compatible within the same major version.
|
|
2
|
+
set(PACKAGE_VERSION "0.1.0")
|
|
3
|
+
if(PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION)
|
|
4
|
+
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
|
5
|
+
else()
|
|
6
|
+
string(REGEX MATCH "^[0-9]+" _wireforge_major "${PACKAGE_VERSION}")
|
|
7
|
+
if(PACKAGE_FIND_VERSION_MAJOR STREQUAL "" OR PACKAGE_FIND_VERSION_MAJOR STREQUAL _wireforge_major)
|
|
8
|
+
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
|
9
|
+
else()
|
|
10
|
+
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
|
11
|
+
endif()
|
|
12
|
+
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
|
|
13
|
+
set(PACKAGE_VERSION_EXACT TRUE)
|
|
14
|
+
endif()
|
|
15
|
+
endif()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# wireforge_generate(<target> PROJECT <dir> [OUTPUT <dir>] [PLUGINS <file>] [TESTS])
|
|
2
|
+
#
|
|
3
|
+
# Runs `wireforge generate` at configure time (again whenever a model file changes) and creates
|
|
4
|
+
# the INTERFACE library <target> with the generated headers, the project's user include
|
|
5
|
+
# directories and wireforge::runtime. With TESTS the GoogleTest project is generated too
|
|
6
|
+
# (<OUTPUT>/<project>/generated_tests). The generator is the `wireforge` command, or
|
|
7
|
+
# `python -m wireforge.cli` when only the Python package is found (WIREFORGE_COMMAND overrides).
|
|
8
|
+
include_guard(GLOBAL)
|
|
9
|
+
|
|
10
|
+
function(_wireforge_command result)
|
|
11
|
+
if(WIREFORGE_COMMAND)
|
|
12
|
+
set(${result} ${WIREFORGE_COMMAND} PARENT_SCOPE)
|
|
13
|
+
return()
|
|
14
|
+
endif()
|
|
15
|
+
find_program(WIREFORGE_EXECUTABLE wireforge)
|
|
16
|
+
if(WIREFORGE_EXECUTABLE)
|
|
17
|
+
set(${result} "${WIREFORGE_EXECUTABLE}" PARENT_SCOPE)
|
|
18
|
+
return()
|
|
19
|
+
endif()
|
|
20
|
+
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
|
21
|
+
set(${result} "${Python3_EXECUTABLE}" -m wireforge.cli PARENT_SCOPE)
|
|
22
|
+
endfunction()
|
|
23
|
+
|
|
24
|
+
function(wireforge_generate target)
|
|
25
|
+
cmake_parse_arguments(PARSE_ARGV 1 arg "TESTS" "PROJECT;OUTPUT;PLUGINS" "")
|
|
26
|
+
if(NOT arg_PROJECT)
|
|
27
|
+
message(FATAL_ERROR "wireforge_generate(${target}): PROJECT is required")
|
|
28
|
+
endif()
|
|
29
|
+
get_filename_component(project "${arg_PROJECT}" ABSOLUTE)
|
|
30
|
+
set(output "${CMAKE_CURRENT_BINARY_DIR}/wireforge_${target}")
|
|
31
|
+
if(arg_OUTPUT)
|
|
32
|
+
get_filename_component(output "${arg_OUTPUT}" ABSOLUTE)
|
|
33
|
+
endif()
|
|
34
|
+
_wireforge_command(command)
|
|
35
|
+
set(arguments generate "${project}" -o "${output}")
|
|
36
|
+
if(arg_TESTS)
|
|
37
|
+
list(APPEND arguments --tests)
|
|
38
|
+
endif()
|
|
39
|
+
if(arg_PLUGINS)
|
|
40
|
+
list(APPEND arguments --plugins "${arg_PLUGINS}")
|
|
41
|
+
endif()
|
|
42
|
+
execute_process(COMMAND ${command} ${arguments} RESULT_VARIABLE failed OUTPUT_VARIABLE log ERROR_VARIABLE log)
|
|
43
|
+
if(failed)
|
|
44
|
+
message(FATAL_ERROR "wireforge_generate(${target}) failed:\n${log}")
|
|
45
|
+
endif()
|
|
46
|
+
file(GLOB_RECURSE model_files CONFIGURE_DEPENDS "${project}/*.yaml")
|
|
47
|
+
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${model_files})
|
|
48
|
+
execute_process(COMMAND ${command} get "${project}" /user_includes/include_dirs --json
|
|
49
|
+
RESULT_VARIABLE missing OUTPUT_VARIABLE answer ERROR_QUIET)
|
|
50
|
+
set(include_dirs "")
|
|
51
|
+
if(NOT missing)
|
|
52
|
+
string(JSON count LENGTH "${answer}" data value)
|
|
53
|
+
if(count GREATER 0)
|
|
54
|
+
math(EXPR last "${count} - 1")
|
|
55
|
+
foreach(index RANGE ${last})
|
|
56
|
+
string(JSON directory GET "${answer}" data value ${index})
|
|
57
|
+
list(APPEND include_dirs "${project}/${directory}")
|
|
58
|
+
endforeach()
|
|
59
|
+
endif()
|
|
60
|
+
endif()
|
|
61
|
+
add_library(${target} INTERFACE)
|
|
62
|
+
target_include_directories(${target} INTERFACE "${output}" ${include_dirs})
|
|
63
|
+
target_link_libraries(${target} INTERFACE wireforge::runtime)
|
|
64
|
+
endfunction()
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Bundle export and import.
|
|
2
|
+
|
|
3
|
+
A bundle is one ordinary single-file project (no `include`) stamped with the current format plus
|
|
4
|
+
`bundle: {content_hash, source_files}`. The sha256 covers the definition content only: stamps, the
|
|
5
|
+
bundle block and key order do not change it.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import io
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, cast
|
|
15
|
+
|
|
16
|
+
from ruamel.yaml import YAML
|
|
17
|
+
from ruamel.yaml.error import YAMLError
|
|
18
|
+
|
|
19
|
+
from wireforge import schema, vectors, versioning
|
|
20
|
+
from wireforge.issues import Issue, errors_only
|
|
21
|
+
from wireforge.loader import SECTIONS, load_project
|
|
22
|
+
from wireforge.plain import to_plain
|
|
23
|
+
from wireforge.validate import Validator, validate_project
|
|
24
|
+
|
|
25
|
+
STAMP_KEYS = frozenset({"schema_version", "schema_minor", "generator", "bundle"})
|
|
26
|
+
ROOT_ONLY_KEYS = ("project", "version", "description", "namespace", "defaults", "user_includes")
|
|
27
|
+
# Standard layout written by import: section -> directory/file (interfaces get one file each).
|
|
28
|
+
LAYOUT = {
|
|
29
|
+
"requirements": "requirements/requirements.yaml",
|
|
30
|
+
"profiles": "profiles/profiles.yaml",
|
|
31
|
+
"messages": "messages/messages.yaml",
|
|
32
|
+
"vectors": "vectors/vectors.yaml",
|
|
33
|
+
}
|
|
34
|
+
TYPE_SECTIONS = ("enums", "types")
|
|
35
|
+
ROOT_SECTIONS = ("defines", "checksums")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class BundleError(Exception):
|
|
39
|
+
"""Export or import refused; `issues` explains why."""
|
|
40
|
+
|
|
41
|
+
def __init__(self, message: str, issues: list[Issue] | None = None) -> None:
|
|
42
|
+
"""Keep the summary and the detailed issues."""
|
|
43
|
+
super().__init__(message)
|
|
44
|
+
self.issues = issues or []
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def content_hash(document: dict[str, Any]) -> str:
|
|
48
|
+
"""sha256 over the definition content (stamps and key order excluded)."""
|
|
49
|
+
content = {key: value for key, value in document.items() if key not in STAMP_KEYS}
|
|
50
|
+
canonical = json.dumps(content, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
|
|
51
|
+
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _yaml() -> YAML:
|
|
55
|
+
yaml = YAML(typ="rt")
|
|
56
|
+
yaml.default_flow_style = False
|
|
57
|
+
yaml.width = 120
|
|
58
|
+
return yaml
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _dump(document: dict[str, Any]) -> str:
|
|
62
|
+
stream = io.StringIO()
|
|
63
|
+
_yaml().dump(document, stream)
|
|
64
|
+
return stream.getvalue()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _stamped(content: dict[str, Any]) -> dict[str, Any]:
|
|
68
|
+
stamp: dict[str, object] = {}
|
|
69
|
+
versioning.stamp(stamp)
|
|
70
|
+
return {**stamp, **content}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def build_bundle(location: Path) -> dict[str, Any]:
|
|
74
|
+
"""Merge a valid project into one bundle document; raises BundleError for invalid projects."""
|
|
75
|
+
result = load_project(location)
|
|
76
|
+
issues = list(result.issues)
|
|
77
|
+
if result.project is not None:
|
|
78
|
+
issues += Validator(result.project).run()
|
|
79
|
+
if not errors_only(issues):
|
|
80
|
+
issues += vectors.check(result.project)
|
|
81
|
+
if result.project is None or errors_only(issues):
|
|
82
|
+
message = f"{location}: export refused, the project has errors"
|
|
83
|
+
raise BundleError(message, errors_only(issues))
|
|
84
|
+
root_file = Path(result.project.source.file)
|
|
85
|
+
root = cast("dict[str, Any]", to_plain(result.documents[str(root_file)]))
|
|
86
|
+
content: dict[str, Any] = {key: root[key] for key in ROOT_ONLY_KEYS if key in root}
|
|
87
|
+
for section in SECTIONS:
|
|
88
|
+
merged: dict[str, Any] = {}
|
|
89
|
+
for document in result.documents.values():
|
|
90
|
+
merged.update(cast("dict[str, Any]", to_plain(document.get(section, {}))))
|
|
91
|
+
if merged:
|
|
92
|
+
content[section] = merged
|
|
93
|
+
sources = [Path(file).relative_to(root_file.parent).as_posix() for file in result.documents]
|
|
94
|
+
bundle = _stamped(content)
|
|
95
|
+
bundle["bundle"] = {"content_hash": content_hash(content), "source_files": sources}
|
|
96
|
+
return bundle
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def export_bundle(location: Path, output: Path) -> None:
|
|
100
|
+
"""Write the bundle of `location` to `output`."""
|
|
101
|
+
output.write_text(_dump(build_bundle(location)), encoding="utf-8")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _load_bundle(path: Path) -> dict[str, Any]:
|
|
105
|
+
try:
|
|
106
|
+
loaded = YAML(typ="safe").load(path.read_text(encoding="utf-8"))
|
|
107
|
+
except (YAMLError, OSError) as error:
|
|
108
|
+
message = f"{path}: cannot read bundle: {error}"
|
|
109
|
+
raise BundleError(message) from error
|
|
110
|
+
if not isinstance(loaded, dict):
|
|
111
|
+
message = f"{path}: a bundle must be a mapping"
|
|
112
|
+
raise BundleError(message)
|
|
113
|
+
return loaded
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _verify(document: dict[str, Any], path: Path) -> None:
|
|
117
|
+
info = document.get("bundle")
|
|
118
|
+
expected = info.get("content_hash") if isinstance(info, dict) else None
|
|
119
|
+
if expected is None:
|
|
120
|
+
message = f"{path}: not a bundle (no bundle.content_hash); use --no-verify to import it"
|
|
121
|
+
raise BundleError(message)
|
|
122
|
+
actual = content_hash(document)
|
|
123
|
+
if actual != expected:
|
|
124
|
+
message = f"{path}: content hash mismatch (bundle says {expected}, content is {actual})"
|
|
125
|
+
raise BundleError(message)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _migrate(document: dict[str, Any], path: Path) -> None:
|
|
129
|
+
try:
|
|
130
|
+
versioning.migrate_document(document)
|
|
131
|
+
except (TypeError, ValueError) as error:
|
|
132
|
+
message = f"{path}: {error}"
|
|
133
|
+
raise BundleError(message) from error
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _split(content: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
|
137
|
+
"""Distribute a single-file project over the standard layout (relative path -> document)."""
|
|
138
|
+
root = {key: content[key] for key in (*ROOT_ONLY_KEYS, *ROOT_SECTIONS) if key in content}
|
|
139
|
+
files: dict[str, dict[str, Any]] = {}
|
|
140
|
+
types = {section: content[section] for section in TYPE_SECTIONS if section in content}
|
|
141
|
+
if types:
|
|
142
|
+
files["types/types.yaml"] = types
|
|
143
|
+
for section, relative in LAYOUT.items():
|
|
144
|
+
if section in content:
|
|
145
|
+
files[relative] = {section: content[section]}
|
|
146
|
+
for name, interface in content.get("interfaces", {}).items():
|
|
147
|
+
files[f"interfaces/{name}.yaml"] = {"interfaces": {name: interface}}
|
|
148
|
+
directories = sorted({relative.split("/")[0] for relative in files})
|
|
149
|
+
if directories:
|
|
150
|
+
root["include"] = [f"{directory}/*.yaml" for directory in directories]
|
|
151
|
+
result = {schema.ROOT_FILE_NAME: _stamped(root)}
|
|
152
|
+
result.update({relative: _stamped(body) for relative, body in files.items()})
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def import_bundle(
|
|
157
|
+
bundle: Path, destination: Path, *, verify: bool = True, single_file: bool = False
|
|
158
|
+
) -> list[Issue]:
|
|
159
|
+
"""Unpack a bundle into `destination` and return the validation issues of the result."""
|
|
160
|
+
if destination.exists() and any(destination.iterdir()):
|
|
161
|
+
message = f"{destination}: destination is not empty"
|
|
162
|
+
raise BundleError(message)
|
|
163
|
+
document = _load_bundle(bundle)
|
|
164
|
+
if verify:
|
|
165
|
+
_verify(document, bundle)
|
|
166
|
+
_migrate(document, bundle)
|
|
167
|
+
content = {key: value for key, value in document.items() if key not in STAMP_KEYS}
|
|
168
|
+
issues = schema.validate_document(_stamped(content), "project.schema.json", file=str(bundle))
|
|
169
|
+
if errors_only(issues):
|
|
170
|
+
message = f"{bundle}: the bundle is not a valid project"
|
|
171
|
+
raise BundleError(message, issues)
|
|
172
|
+
files = {schema.ROOT_FILE_NAME: _stamped(content)} if single_file else _split(content)
|
|
173
|
+
for relative, body in files.items():
|
|
174
|
+
target = destination / relative
|
|
175
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
176
|
+
target.write_text(_dump(body), encoding="utf-8")
|
|
177
|
+
return validate_project(destination)
|