misch 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.
- misch-0.1.0/.github/workflows/ci.yml +54 -0
- misch-0.1.0/.github/workflows/release.yml +37 -0
- misch-0.1.0/.gitignore +26 -0
- misch-0.1.0/CHANGELOG.md +31 -0
- misch-0.1.0/CONTRIBUTING.md +44 -0
- misch-0.1.0/LICENSE +21 -0
- misch-0.1.0/PKG-INFO +138 -0
- misch-0.1.0/README.md +110 -0
- misch-0.1.0/docs/DESIGN.md +115 -0
- misch-0.1.0/docs/rule-texts.md +48 -0
- misch-0.1.0/pyproject.toml +57 -0
- misch-0.1.0/src/misch/__init__.py +3 -0
- misch-0.1.0/src/misch/cli.py +352 -0
- misch-0.1.0/src/misch/config.py +127 -0
- misch-0.1.0/src/misch/db/__init__.py +178 -0
- misch-0.1.0/src/misch/engine/__init__.py +3 -0
- misch-0.1.0/src/misch/engine/cppcheck.py +149 -0
- misch-0.1.0/src/misch/report/__init__.py +1 -0
- misch-0.1.0/src/misch/report/baseline.py +82 -0
- misch-0.1.0/src/misch/report/dev_render.py +108 -0
- misch-0.1.0/src/misch/report/deviations.py +235 -0
- misch-0.1.0/src/misch/report/headlines.py +76 -0
- misch-0.1.0/src/misch/report/model.py +99 -0
- misch-0.1.0/src/misch/report/renderers.py +229 -0
- misch-0.1.0/src/misch/scaffold.py +99 -0
- misch-0.1.0/tests/test_core.py +587 -0
- misch-0.1.0/tests/test_e2e.py +187 -0
- misch-0.1.0/uv.lock +321 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: astral-sh/setup-uv@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.13"
|
|
21
|
+
enable-cache: true
|
|
22
|
+
- run: uv sync --locked
|
|
23
|
+
- run: uv run ruff check
|
|
24
|
+
# Catch packaging breaks (bad metadata, missing files) before a release.
|
|
25
|
+
- run: uv build
|
|
26
|
+
|
|
27
|
+
test:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
strategy:
|
|
30
|
+
fail-fast: false
|
|
31
|
+
matrix:
|
|
32
|
+
python-version: ["3.11", "3.13", "3.14"]
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
# cppcheck (with its bundled misra.py addon) powers the end-to-end tests;
|
|
36
|
+
# without it they skip, so it must be present for real coverage here.
|
|
37
|
+
- name: Install cppcheck
|
|
38
|
+
run: sudo apt-get update && sudo apt-get install -y cppcheck
|
|
39
|
+
- name: Check cppcheck + misra addon are available
|
|
40
|
+
run: |
|
|
41
|
+
cppcheck --version
|
|
42
|
+
# Addon install paths vary by distro (Debian/Ubuntu use
|
|
43
|
+
# /usr/lib/<arch>/cppcheck/addons), so probe functionally: the
|
|
44
|
+
# addon must produce a misra finding on a known violation.
|
|
45
|
+
printf 'int f(int x){if(x>0){return 1;}return 0;}\n' > /tmp/misra_probe.c
|
|
46
|
+
cppcheck --enable=style --addon=misra /tmp/misra_probe.c 2>&1 \
|
|
47
|
+
| grep -q "misra-c2012"
|
|
48
|
+
- uses: astral-sh/setup-uv@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: ${{ matrix.python-version }}
|
|
51
|
+
enable-cache: true
|
|
52
|
+
- run: uv sync --locked
|
|
53
|
+
# Coverage is reported for visibility, not (yet) gated on a threshold.
|
|
54
|
+
- run: uv run pytest -q --cov=misch --cov-report=term
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI via trusted publishing (no API token: configure the
|
|
4
|
+
# "pending publisher" for this repo/workflow on pypi.org first).
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
- name: Check tag matches project version
|
|
16
|
+
run: |
|
|
17
|
+
version=$(uv version --short)
|
|
18
|
+
test "v$version" = "${GITHUB_REF_NAME}" || {
|
|
19
|
+
echo "tag ${GITHUB_REF_NAME} != pyproject version $version"; exit 1; }
|
|
20
|
+
- run: uv build
|
|
21
|
+
- uses: actions/upload-artifact@v4
|
|
22
|
+
with:
|
|
23
|
+
name: dist
|
|
24
|
+
path: dist/
|
|
25
|
+
|
|
26
|
+
publish:
|
|
27
|
+
needs: build
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment: pypi
|
|
30
|
+
permissions:
|
|
31
|
+
id-token: write # trusted publishing
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/download-artifact@v4
|
|
34
|
+
with:
|
|
35
|
+
name: dist
|
|
36
|
+
path: dist/
|
|
37
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
misch-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.venv/
|
|
5
|
+
venv/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
|
|
10
|
+
# uv
|
|
11
|
+
.uv/
|
|
12
|
+
|
|
13
|
+
# Tooling caches
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
|
|
17
|
+
# Harness artefacts (findings, generated compile DBs)
|
|
18
|
+
build_analysis/
|
|
19
|
+
*.misra.json
|
|
20
|
+
|
|
21
|
+
# Bring-your-own rule texts must never be committed
|
|
22
|
+
misra_c_2023_headlines_for_cppcheck.txt
|
|
23
|
+
|
|
24
|
+
# Local work tracking, not part of the public tree
|
|
25
|
+
TODO.md
|
|
26
|
+
.coverage
|
misch-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-07-07
|
|
10
|
+
|
|
11
|
+
First public release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `misch run`: analyse a C project via cppcheck + the `misra.py` addon, driven off a normalised `compile_commands.json`. Categorised rich-terminal report (rule table + summary by default; `-v`/`--verbose` adds the per-location listing) and deterministic JSON output; non-zero exit on findings. Interpolated paths and messages are escaped and emoji substitution is disabled, so a `:100:` line number is never rendered as an emoji.
|
|
16
|
+
- Compile-DB sources: `existing`, `meson`, and `cmake` (paths normalised to absolute up front). Plain-Make projects can generate a DB with an interceptor (e.g. `bear -- make`) and use `existing`.
|
|
17
|
+
- Scope control: `scope` / `exclude` globs with a hard failure on any unattributed file, so nothing is ever silently ignored.
|
|
18
|
+
- Bring-your-own MISRA rule texts (`$MISRA_RULE_TEXTS` > `[rules].texts`); headlines and Mandatory/Required/Advisory categories parsed from them. No copyrighted material is bundled.
|
|
19
|
+
- `misch init`: generate a commented `misra.toml`, with flags to pre-fill every section.
|
|
20
|
+
- `misch baseline` and `misch run --baseline`: count-based ratchet keyed on a line-independent fingerprint; report all findings but fail only on new ones.
|
|
21
|
+
- `misch deviations`: grammar-aware harvest of every `cppcheck-suppress*` form, justification enforcement, rule-id validation against the headlines, project suppressions-file parsing, and a terminal + Markdown deviation record. `--check-stale` cross-references an unsuppressed run to flag suppressions that hide nothing.
|
|
22
|
+
- SARIF 2.1.0 output (`--format sarif`) for GitHub code scanning and IDE annotations, with MISRA category mapped to error/warning levels.
|
|
23
|
+
- Coloured `--help` output via `rich-argparse` (auto-disables when stdout is not a TTY or `NO_COLOR` is set).
|
|
24
|
+
- `misch --version`.
|
|
25
|
+
- Release pipeline: `uv build` in CI, and a tag-triggered GitHub Actions workflow publishing to PyPI via trusted publishing.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Findings at in-tree locations matching neither `scope` nor `exclude` are now a hard error (exit 2, offending files listed) instead of being silently dropped. The compile DB only lists translation units, so the scope-coverage gate never sees headers; previously a public-header directory left out of `scope` simply vanished from the audit. Findings at explicitly excluded paths and in system headers outside the tree are still dropped silently by design.
|
|
30
|
+
- Error messages no longer lose TOML section names to rich markup: `[project].scope`, `[project].exclude`, and `[rules].texts` were being parsed as style tags and dropped from the scope-error and missing-rule-texts notices.
|
|
31
|
+
- `misch run` now applies the project suppressions file (`[deviations].suppressions`) to the analysis via cppcheck `--suppressions-list`, so a project-level deviation actually silences the findings it names. Previously the file was only harvested into the deviation record by `misch deviations`; findings it covered still surfaced and failed the run. The suppressions file is dropped on the `--check-stale` pass (alongside inline `cppcheck-suppress` comments) so staleness detection still sees what each entry hides.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Contributing to misch
|
|
2
|
+
|
|
3
|
+
`misch` is a small, focused tool: a config-driven MISRA C:2023 harness around cppcheck. Contributions are held to that bar: dependency-light, tested, and honest about what the analysis can and cannot prove.
|
|
4
|
+
|
|
5
|
+
## Dev setup
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
uv sync # install the project + dev deps into .venv
|
|
9
|
+
uv run misch --help
|
|
10
|
+
uv run pytest -q # unit tests (no cppcheck needed)
|
|
11
|
+
uv run ruff check . # lint
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`cppcheck` (with its bundled `misra.py` addon) must be on `PATH` for the `run`/`baseline` commands and any end-to-end test.
|
|
15
|
+
|
|
16
|
+
## Architecture in one paragraph
|
|
17
|
+
|
|
18
|
+
`compile_commands.json` is the universal seam; a single internal `Finding` model backs every output. The pipeline is `resolve config → obtain + normalise compile DB → scope-check → cppcheck (XML) → Finding model → classify → render`. See [`docs/DESIGN.md`](docs/DESIGN.md) for the full picture. Keep renderers pure (model in, bytes out) so the terminal, JSON, baseline, and deviation views can never disagree.
|
|
19
|
+
|
|
20
|
+
## Where things live
|
|
21
|
+
|
|
22
|
+
- `src/misch/cli.py`: argument parsing and command handlers.
|
|
23
|
+
- `config.py`: `misra.toml` loading and path/rule-texts resolution.
|
|
24
|
+
- `db/`: compile-DB resolution, normalisation, and scope classification.
|
|
25
|
+
- `engine/cppcheck.py`: the only place that shells out to cppcheck; parses XML into Findings.
|
|
26
|
+
- `report/`: the `Finding` model, headlines parser, baseline, deviation harvester, and renderers.
|
|
27
|
+
- `scaffold.py`: the `init` config template.
|
|
28
|
+
|
|
29
|
+
## Adding an engine or a DB normaliser
|
|
30
|
+
|
|
31
|
+
- **A new engine** implements the same contract as `engine/cppcheck.run`: take the config + compile DB, return `list[Finding]`. Nothing downstream should know which engine produced a finding.
|
|
32
|
+
- **A DB normaliser** (e.g. a cross-toolchain flag translator) transforms the compile DB before analysis. Keep it optional and selected by config, so the common native case needs none.
|
|
33
|
+
|
|
34
|
+
## Rules of the house
|
|
35
|
+
|
|
36
|
+
- **No bundled MISRA material.** The rule-texts file is bring-your-own and gitignored; never commit it or paraphrase copyrighted rule text into the tree.
|
|
37
|
+
- **Python ≥ 3.11**, standard library first. New runtime dependencies need a good reason.
|
|
38
|
+
- **Determinism.** Sort output by `(rule, file, line)`; keep JSON key order stable. No non-deterministic tests.
|
|
39
|
+
- **Be honest about coverage.** cppcheck checks a subset of MISRA C:2023; if a feature is best-effort (e.g. staleness), say so in the docstring and the docs rather than overclaim.
|
|
40
|
+
- Run `ruff check .` and `pytest` before opening a PR. Add a test for every bug fix and every feature.
|
|
41
|
+
|
|
42
|
+
## Commits
|
|
43
|
+
|
|
44
|
+
Use [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `doc:`, `test:`, `chore:`, `refactor:`). Explain _why_ in the body, not _what_ the diff already shows.
|
misch-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aaron James Long
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
misch-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: misch
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Config-driven MISRA C:2023 static-analysis harness for arbitrary C projects (cppcheck-backed, bring-your-own rule texts).
|
|
5
|
+
Project-URL: Repository, https://github.com/aajll/misch
|
|
6
|
+
Project-URL: Changelog, https://github.com/aajll/misch/blob/master/CHANGELOG.md
|
|
7
|
+
Project-URL: Issues, https://github.com/aajll/misch/issues
|
|
8
|
+
Author: Aaron James Long
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: c,cppcheck,embedded,misra,safety,static-analysis
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: C
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: rich-argparse>=1.5
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# misch
|
|
30
|
+
|
|
31
|
+
[](https://github.com/aajll/misch/actions/workflows/ci.yml)
|
|
32
|
+
[](https://pypi.org/project/misch/)
|
|
33
|
+
[](https://pypi.org/project/misch/)
|
|
34
|
+
[](LICENSE)
|
|
35
|
+
|
|
36
|
+
**Point it at any C project and ratchet it toward MISRA C:2023.** `misch` (MISra-CHeck) is a config-driven static-analysis harness: it wraps [cppcheck](https://cppcheck.sourceforge.io/) and its `misra.py` addon, drives them off a real `compile_commands.json`, and turns per-project difference into a small `misra.toml` instead of a forked shell script.
|
|
37
|
+
|
|
38
|
+
- **Bring-your-own rule texts:** ships no copyrighted MISRA material (see [`docs/rule-texts.md`](docs/rule-texts.md)).
|
|
39
|
+
- **Baseline / ratchet:** adopt MISRA on an existing codebase without a day-one wall of findings; fail CI only on _new_ ones.
|
|
40
|
+
- **Deviation records:** harvests every `cppcheck-suppress`, enforces a justification, and emits an audit-ready record.
|
|
41
|
+
- **No silent scope creep:** every file is consciously analysed or excluded, or the run fails.
|
|
42
|
+
|
|
43
|
+
> `misch` is a **guidance + ratchet** tool, not a compliance-certification tool: cppcheck's addon covers a subset of MISRA C:2023. The engine sits behind an interface so a certified analyser can slot in later. See [`docs/DESIGN.md`](docs/DESIGN.md).
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
Requires Python ≥ 3.11 and `cppcheck` (with its bundled `misra.py` addon) on `PATH`. Tested with cppcheck 2.21; any recent release with the `misra.py` addon should work.
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
uv tool install misch # or: pipx install misch / pip install misch
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
From a checkout: `uv tool install .` (or `uv sync && uv run misch ...`).
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
cd /path/to/your/c-project
|
|
59
|
+
misch init # write a commented misra.toml (flags pre-fill it)
|
|
60
|
+
misch run # analyse; categorised report; non-zero exit on findings
|
|
61
|
+
misch baseline # accept current findings as the baseline
|
|
62
|
+
misch run --baseline # from now on, fail only on NEW findings
|
|
63
|
+
misch deviations # audit every suppression + its justification
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Bring your own rule texts (optional, for headlines + Mandatory/Required/Advisory):
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
export MISRA_RULE_TEXTS=/path/to/misra_c_2023_headlines_for_cppcheck.txt
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A run against a project with findings looks like this (headline text comes from your own rule-texts file):
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
──────────────────────────────── MISRA analysis ────────────────────────────────
|
|
76
|
+
scope: 1 analysed 0 excluded files with findings: 1
|
|
77
|
+
|
|
78
|
+
Findings by rule
|
|
79
|
+
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
80
|
+
┃ Rule ┃ Category ┃ Count ┃ Headline ┃
|
|
81
|
+
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
|
|
82
|
+
│ misra-c2012-8.4 │ required │ 2 │ Headline from your licensed MISRA copy │
|
|
83
|
+
│ misra-c2012-17.7 │ required │ 1 │ Headline from your licensed MISRA copy │
|
|
84
|
+
│ misra-c2012-21.6 │ required │ 1 │ Headline from your licensed MISRA copy │
|
|
85
|
+
│ misra-c2012-15.5 │ advisory │ 1 │ Headline from your licensed MISRA copy │
|
|
86
|
+
│ misra-c2012-8.7 │ advisory │ 1 │ Headline from your licensed MISRA copy │
|
|
87
|
+
└──────────────────┴──────────┴───────┴────────────────────────────────────────┘
|
|
88
|
+
|
|
89
|
+
Run with -v/--verbose for the per-location listing.
|
|
90
|
+
|
|
91
|
+
6 MISRA finding(s): 4 required 2 advisory
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Commands
|
|
95
|
+
|
|
96
|
+
| Command | Purpose |
|
|
97
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
98
|
+
| `misch init` | Generate a commented `misra.toml`; flags (`--db`, `--scope`, `--exclude`, `--platform`, `--rule-texts`, …) pre-fill each section. |
|
|
99
|
+
| `misch run` | Analyse. Rule table + summary (add `-v` for per-location detail, or `--format json`/`--format sarif`). Exit 1 on findings; `--baseline` fails only on new ones. |
|
|
100
|
+
| `misch baseline` | Snapshot the current findings as the accepted baseline. |
|
|
101
|
+
| `misch deviations` | Harvest `cppcheck-suppress*`, enforce justifications, validate rule ids, emit a Markdown deviation record (`--format md`). |
|
|
102
|
+
|
|
103
|
+
Exit codes are CI-friendly and format-independent: `0` clean (or no new findings under `--baseline`), `1` findings (or unjustified/unknown deviations), `2` config, compile-DB, scope, or engine error.
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
`misch init` writes a documented `misra.toml`; the essentials:
|
|
108
|
+
|
|
109
|
+
```toml
|
|
110
|
+
[project]
|
|
111
|
+
scope = ["src/", "include/"] # analysed
|
|
112
|
+
exclude = ["tests/", "subprojects/"] # explicitly out of scope
|
|
113
|
+
|
|
114
|
+
[db]
|
|
115
|
+
source = "meson" # meson | cmake | existing
|
|
116
|
+
# Plain-Make projects: generate a DB (e.g. `bear -- make`) and use "existing".
|
|
117
|
+
|
|
118
|
+
[platform]
|
|
119
|
+
preset = "unix64" # cppcheck built-in, or [platform].xml
|
|
120
|
+
|
|
121
|
+
[rules]
|
|
122
|
+
texts = "${MISRA_RULE_TEXTS}" # bring-your-own; optional
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## How it works
|
|
126
|
+
|
|
127
|
+
`compile_commands.json` is the universal seam: every build-system and toolchain concern collapses into "produce a normalised compile DB". A single internal Finding model backs every output (terminal, JSON, baseline diff, deviation record), so they can never disagree. Full architecture and rationale in [`docs/DESIGN.md`](docs/DESIGN.md).
|
|
128
|
+
|
|
129
|
+
## Documentation
|
|
130
|
+
|
|
131
|
+
- [`docs/DESIGN.md`](docs/DESIGN.md): architecture, the scope and deviation strategy.
|
|
132
|
+
- [`docs/rule-texts.md`](docs/rule-texts.md): bringing your own MISRA headlines.
|
|
133
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md): dev setup, tests, adding engines/normalisers.
|
|
134
|
+
- [`CHANGELOG.md`](CHANGELOG.md): release notes.
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT (see [`LICENSE`](LICENSE)). `misch` contains and ships no MISRA material; rule texts are bring-your-own from your licensed copy.
|
misch-0.1.0/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# misch
|
|
2
|
+
|
|
3
|
+
[](https://github.com/aajll/misch/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/misch/)
|
|
5
|
+
[](https://pypi.org/project/misch/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
**Point it at any C project and ratchet it toward MISRA C:2023.** `misch` (MISra-CHeck) is a config-driven static-analysis harness: it wraps [cppcheck](https://cppcheck.sourceforge.io/) and its `misra.py` addon, drives them off a real `compile_commands.json`, and turns per-project difference into a small `misra.toml` instead of a forked shell script.
|
|
9
|
+
|
|
10
|
+
- **Bring-your-own rule texts:** ships no copyrighted MISRA material (see [`docs/rule-texts.md`](docs/rule-texts.md)).
|
|
11
|
+
- **Baseline / ratchet:** adopt MISRA on an existing codebase without a day-one wall of findings; fail CI only on _new_ ones.
|
|
12
|
+
- **Deviation records:** harvests every `cppcheck-suppress`, enforces a justification, and emits an audit-ready record.
|
|
13
|
+
- **No silent scope creep:** every file is consciously analysed or excluded, or the run fails.
|
|
14
|
+
|
|
15
|
+
> `misch` is a **guidance + ratchet** tool, not a compliance-certification tool: cppcheck's addon covers a subset of MISRA C:2023. The engine sits behind an interface so a certified analyser can slot in later. See [`docs/DESIGN.md`](docs/DESIGN.md).
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
Requires Python ≥ 3.11 and `cppcheck` (with its bundled `misra.py` addon) on `PATH`. Tested with cppcheck 2.21; any recent release with the `misra.py` addon should work.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
uv tool install misch # or: pipx install misch / pip install misch
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
From a checkout: `uv tool install .` (or `uv sync && uv run misch ...`).
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
cd /path/to/your/c-project
|
|
31
|
+
misch init # write a commented misra.toml (flags pre-fill it)
|
|
32
|
+
misch run # analyse; categorised report; non-zero exit on findings
|
|
33
|
+
misch baseline # accept current findings as the baseline
|
|
34
|
+
misch run --baseline # from now on, fail only on NEW findings
|
|
35
|
+
misch deviations # audit every suppression + its justification
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Bring your own rule texts (optional, for headlines + Mandatory/Required/Advisory):
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
export MISRA_RULE_TEXTS=/path/to/misra_c_2023_headlines_for_cppcheck.txt
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
A run against a project with findings looks like this (headline text comes from your own rule-texts file):
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
──────────────────────────────── MISRA analysis ────────────────────────────────
|
|
48
|
+
scope: 1 analysed 0 excluded files with findings: 1
|
|
49
|
+
|
|
50
|
+
Findings by rule
|
|
51
|
+
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
52
|
+
┃ Rule ┃ Category ┃ Count ┃ Headline ┃
|
|
53
|
+
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
|
|
54
|
+
│ misra-c2012-8.4 │ required │ 2 │ Headline from your licensed MISRA copy │
|
|
55
|
+
│ misra-c2012-17.7 │ required │ 1 │ Headline from your licensed MISRA copy │
|
|
56
|
+
│ misra-c2012-21.6 │ required │ 1 │ Headline from your licensed MISRA copy │
|
|
57
|
+
│ misra-c2012-15.5 │ advisory │ 1 │ Headline from your licensed MISRA copy │
|
|
58
|
+
│ misra-c2012-8.7 │ advisory │ 1 │ Headline from your licensed MISRA copy │
|
|
59
|
+
└──────────────────┴──────────┴───────┴────────────────────────────────────────┘
|
|
60
|
+
|
|
61
|
+
Run with -v/--verbose for the per-location listing.
|
|
62
|
+
|
|
63
|
+
6 MISRA finding(s): 4 required 2 advisory
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Commands
|
|
67
|
+
|
|
68
|
+
| Command | Purpose |
|
|
69
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `misch init` | Generate a commented `misra.toml`; flags (`--db`, `--scope`, `--exclude`, `--platform`, `--rule-texts`, …) pre-fill each section. |
|
|
71
|
+
| `misch run` | Analyse. Rule table + summary (add `-v` for per-location detail, or `--format json`/`--format sarif`). Exit 1 on findings; `--baseline` fails only on new ones. |
|
|
72
|
+
| `misch baseline` | Snapshot the current findings as the accepted baseline. |
|
|
73
|
+
| `misch deviations` | Harvest `cppcheck-suppress*`, enforce justifications, validate rule ids, emit a Markdown deviation record (`--format md`). |
|
|
74
|
+
|
|
75
|
+
Exit codes are CI-friendly and format-independent: `0` clean (or no new findings under `--baseline`), `1` findings (or unjustified/unknown deviations), `2` config, compile-DB, scope, or engine error.
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
`misch init` writes a documented `misra.toml`; the essentials:
|
|
80
|
+
|
|
81
|
+
```toml
|
|
82
|
+
[project]
|
|
83
|
+
scope = ["src/", "include/"] # analysed
|
|
84
|
+
exclude = ["tests/", "subprojects/"] # explicitly out of scope
|
|
85
|
+
|
|
86
|
+
[db]
|
|
87
|
+
source = "meson" # meson | cmake | existing
|
|
88
|
+
# Plain-Make projects: generate a DB (e.g. `bear -- make`) and use "existing".
|
|
89
|
+
|
|
90
|
+
[platform]
|
|
91
|
+
preset = "unix64" # cppcheck built-in, or [platform].xml
|
|
92
|
+
|
|
93
|
+
[rules]
|
|
94
|
+
texts = "${MISRA_RULE_TEXTS}" # bring-your-own; optional
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## How it works
|
|
98
|
+
|
|
99
|
+
`compile_commands.json` is the universal seam: every build-system and toolchain concern collapses into "produce a normalised compile DB". A single internal Finding model backs every output (terminal, JSON, baseline diff, deviation record), so they can never disagree. Full architecture and rationale in [`docs/DESIGN.md`](docs/DESIGN.md).
|
|
100
|
+
|
|
101
|
+
## Documentation
|
|
102
|
+
|
|
103
|
+
- [`docs/DESIGN.md`](docs/DESIGN.md): architecture, the scope and deviation strategy.
|
|
104
|
+
- [`docs/rule-texts.md`](docs/rule-texts.md): bringing your own MISRA headlines.
|
|
105
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md): dev setup, tests, adding engines/normalisers.
|
|
106
|
+
- [`CHANGELOG.md`](CHANGELOG.md): release notes.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT (see [`LICENSE`](LICENSE)). `misch` contains and ships no MISRA material; rule texts are bring-your-own from your licensed copy.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# misch design
|
|
2
|
+
|
|
3
|
+
A standalone, config-driven harness that runs MISRA C:2023 static analysis on **arbitrary C projects** and guides them toward the standard. It wraps [cppcheck](https://cppcheck.sourceforge.io/) and its `misra.py` addon, driven off a real `compile_commands.json` so the checker sees exactly the include paths and defines the compiler sees.
|
|
4
|
+
|
|
5
|
+
It ships **no copyrighted MISRA material**. The rule-texts (headlines) file is bring-your-own; the harness supplies only the machinery.
|
|
6
|
+
|
|
7
|
+
## Why this exists
|
|
8
|
+
|
|
9
|
+
Firmware/controller projects each grew their own `misra_static_analysis.sh` (cppcheck + addon + platform + suppressions), and those scripts copy-pasted and diverged. Public primitive libraries can't bundle any MISRA rule material at all. This harness factors the shared machinery out once, keeps every copyrighted artifact out of the tree, and turns per-project difference into a small `misra.toml` instead of a forked script.
|
|
10
|
+
|
|
11
|
+
## Core architecture
|
|
12
|
+
|
|
13
|
+
Two ideas drive everything:
|
|
14
|
+
|
|
15
|
+
1. **`compile_commands.json` is the universal seam.** All project-specific concerns (build system, cross toolchain, flag dialects) collapse into "produce a normalised compile DB." The harness core consumes a compile DB and nothing else, so "point it at any C project" is real.
|
|
16
|
+
2. **One internal Finding model; outputs are pure renderers.** cppcheck is always run in XML mode and normalised into a single `Finding` model. The terminal view, JSON, the baseline diff, the scope-coverage report and the deviation record are all projections of that one model, so they can never disagree.
|
|
17
|
+
|
|
18
|
+
### Pipeline
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
resolve config
|
|
22
|
+
→ obtain compile DB (source: existing | meson | cmake)
|
|
23
|
+
→ normalise (base; ti-c2000 plugin later)
|
|
24
|
+
→ run cppcheck (platform + misra addon + suppressions), XML out
|
|
25
|
+
→ parse → Finding model
|
|
26
|
+
→ classify (category from BYO headlines)
|
|
27
|
+
→ scope-coverage check (fail on unattributed files)
|
|
28
|
+
→ baseline diff (optional)
|
|
29
|
+
→ render (terminal default; json/md/sarif on request)
|
|
30
|
+
→ exit code (findings → non-zero, or new-only under --baseline)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Rule-texts (headlines): bring-your-own
|
|
34
|
+
|
|
35
|
+
The cppcheck misra addon runs its checks regardless; the headlines file only makes output human-readable and carries each rule's category (Mandatory / Required / Advisory). We do **not** bundle it. Resolution precedence:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
$MISRA_RULE_TEXTS > misra.toml [rules].texts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
With neither set, analysis still runs but findings are tagged `category: unknown` and a one-line notice points at `docs/rule-texts.md`. Rule **classification** is parsed from this file too, so we never ship a second copyrighted table.
|
|
42
|
+
|
|
43
|
+
## Configuration (`misra.toml`)
|
|
44
|
+
|
|
45
|
+
Everything that differs between projects is data, not shell:
|
|
46
|
+
|
|
47
|
+
```toml
|
|
48
|
+
[project]
|
|
49
|
+
scope = ["components/", "src/"] # analysed
|
|
50
|
+
exclude = ["vendor/", "subprojects/", "src/generated/", "tests/"]
|
|
51
|
+
|
|
52
|
+
[db]
|
|
53
|
+
source = "meson" # meson | cmake | existing
|
|
54
|
+
# Plain-Make projects: generate a DB (e.g. `bear -- make`) and use "existing".
|
|
55
|
+
# path = "build/compile_commands.json" # for source = "existing"
|
|
56
|
+
|
|
57
|
+
[platform]
|
|
58
|
+
preset = "unix64" # cppcheck built-in, or a custom XML path
|
|
59
|
+
# xml = "analysis/c2000_platform.xml"
|
|
60
|
+
|
|
61
|
+
[toolchain]
|
|
62
|
+
defines = [] # e.g. TI keyword neutralisation on cross targets
|
|
63
|
+
|
|
64
|
+
[rules]
|
|
65
|
+
texts = "${MISRA_RULE_TEXTS}" # BYO; env expands
|
|
66
|
+
|
|
67
|
+
[report]
|
|
68
|
+
outputs = ["terminal"] # + {format="json", path="build_analysis/misra.json"}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Scope: protecting non-audited content
|
|
72
|
+
|
|
73
|
+
The failure mode is silent scope creep, so **exclusion is explicit and enumerated, never implicit**:
|
|
74
|
+
|
|
75
|
+
- Central `scope`/`exclude` globs in `misra.toml`: one reviewable boundary, not scattered markers.
|
|
76
|
+
- Each exclude glob applies at **both** levels from one key: skip the TU (`-i`) *and* drop findings whose location falls in the excluded path (findings can leak from an excluded header included by an in-scope source).
|
|
77
|
+
- A **scope-coverage report** buckets every file in the compile DB into `{analysed, excluded-by-rule, unattributed}` and makes `unattributed` a **hard error**. A newly added `tests/` (or anything) must match an exclude rule or fail CI; nothing is implicitly ignored.
|
|
78
|
+
- The compile DB only lists translation units, so the coverage check cannot see **headers**: they enter the analysis via inclusion and findings land at their locations. A finding at an in-tree location matching neither `scope` nor `exclude` is therefore also a **hard error** -- a public-header directory left out of `scope` must not silently vanish from the audit. Findings at explicitly excluded paths, and in system/toolchain headers outside the tree, are dropped silently by design. This gate fires only when something is actually found at the unclassified location; a clean forgotten header produces nothing to drop, so full header coverage would need include-graph extraction (possible future work).
|
|
79
|
+
|
|
80
|
+
## Deviations: harvesting `cppcheck-suppress*`
|
|
81
|
+
|
|
82
|
+
Every inline suppression is a MISRA deviation and must land in the deviation record with a rationale.
|
|
83
|
+
|
|
84
|
+
- **Grammar-aware harvest** of in-scope sources: `cppcheck-suppress <id>`, bracketed `cppcheck-suppress[id1,id2]`, and `-suppress-begin/-end/-file/-macro`. Project standardises on the bracketed form.
|
|
85
|
+
- **Justification required** via a structured tag; the run **fails** if any inline suppression lacks one:
|
|
86
|
+
```c
|
|
87
|
+
/* cppcheck-suppress misra-c2012-11.4 ; @deviation MSGRAM base is a device-fixed literal */
|
|
88
|
+
```
|
|
89
|
+
- **Validate ids** against the real rule set (from the headlines file) to catch typos that silently suppress nothing.
|
|
90
|
+
- **Staleness**: `--check-stale` runs the engine with inline suppression disabled, then flags any harvested `cppcheck-suppress` whose rule has no finding at its site. A dead deviation is an audit smell. (This cross-reference sidesteps cppcheck's `unmatchedSuppression`, which it does not reliably emit under `--project`.)
|
|
91
|
+
- **Merge** inline + project-level `suppressions.txt` into one categorised, justified record (scope / advisory-deviation / false-positive / inline-pointer) → the deviation report.
|
|
92
|
+
|
|
93
|
+
## Outputs
|
|
94
|
+
|
|
95
|
+
- **Default: rich terminal** (rule table grouped by category, plus summary counts; `-v` adds the per-location listing). Interpolated data is escaped and emoji substitution is off so line numbers cannot be misread as emoji. No file unless asked.
|
|
96
|
+
- **Opt-in file formats** via `--format`/`--output` or `[report].outputs`:
|
|
97
|
+
- `json`: canonical, stable key order; substrate for baseline + deviation record; `jq`/`grep`-friendly.
|
|
98
|
+
- `md`: PR summary and the audit/deviation doc.
|
|
99
|
+
- `sarif`: SARIF 2.1.0 for GitHub code scanning / IDE annotations.
|
|
100
|
+
- `junit`: optional CI panels (planned).
|
|
101
|
+
- Deterministic sort `(rule, file, line)`; artifacts under the build dir, gitignored. Exit code is format-independent.
|
|
102
|
+
|
|
103
|
+
## Honest scope
|
|
104
|
+
|
|
105
|
+
cppcheck's addon covers a subset of MISRA C:2023 (many rules are undecidable without a certified analyser). This is a **guidance + ratchet** tool, not a compliance-certification tool, which matches the goal. The engine sits behind an interface so a commercial tool (Axivion / Parasoft / PC-lint) can slot in later behind the same config and report format.
|
|
106
|
+
|
|
107
|
+
## Status
|
|
108
|
+
|
|
109
|
+
The analysis pipeline, config scaffolding (`init`), baseline/ratchet, and the deviation harvester/record are all implemented and dogfooded on the `ucrc` primitive. Planned work (cross-target platform presets, toolchain normaliser plugins, JUnit output) is tracked in the issue tracker.
|
|
110
|
+
|
|
111
|
+
## Non-goals
|
|
112
|
+
|
|
113
|
+
- Not a certified compliance tool.
|
|
114
|
+
- Does not bundle, reproduce, or derive MISRA rule text.
|
|
115
|
+
- Does not require a specific build system (compile DB is the seam).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Bring-your-own MISRA rule texts (headlines)
|
|
2
|
+
|
|
3
|
+
`misch` ships **no MISRA material**. The MISRA C:2023 guideline text is copyrighted by The MISRA Consortium and must not be redistributed, so the harness never bundles, reproduces, or derives it.
|
|
4
|
+
|
|
5
|
+
## What the file is for
|
|
6
|
+
|
|
7
|
+
cppcheck's `misra.py` addon runs its **checks** without any rule-texts file; it just reports rule numbers (`misra-c2012-11.4`). The rule-texts file adds two things the harness uses for display and classification only:
|
|
8
|
+
|
|
9
|
+
- a short **headline** per rule, and
|
|
10
|
+
- the guideline **category** (Mandatory / Required / Advisory).
|
|
11
|
+
|
|
12
|
+
Without it, analysis still runs; findings are simply tagged `category: unknown`.
|
|
13
|
+
|
|
14
|
+
## How to provide it
|
|
15
|
+
|
|
16
|
+
Create the file from your own licensed copy of the MISRA C:2023 document, in the [cppcheck rule-texts format](https://cppcheck.sourceforge.io/misra.php), then point the harness at it by either exporting an environment variable:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
export MISRA_RULE_TEXTS=/path/to/misra_c_2023_headlines_for_cppcheck.txt
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
or naming it in `misra.toml`:
|
|
23
|
+
|
|
24
|
+
```toml
|
|
25
|
+
[rules]
|
|
26
|
+
texts = "${MISRA_RULE_TEXTS}" # env-expanded
|
|
27
|
+
# texts = "analysis/headlines.txt" # or a path relative to the config
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Resolution precedence is `$MISRA_RULE_TEXTS` → `[rules].texts`.
|
|
31
|
+
|
|
32
|
+
## In CI
|
|
33
|
+
|
|
34
|
+
Inject the file as a secret (or fetch it from a private, licensed location) and export `MISRA_RULE_TEXTS` before `misch run`. It must never be committed to a public repository; `.gitignore` already excludes the conventional filename.
|
|
35
|
+
|
|
36
|
+
## Format the parser expects
|
|
37
|
+
|
|
38
|
+
Tolerant and best-effort. Each rule block begins with a line like:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Appendix A Summary of guidelines
|
|
42
|
+
Rule 11.4 Advisory
|
|
43
|
+
A conversion should not be performed between a pointer to object and an integer type
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
or `Dir 4.9 Advisory` for directives. The category token (`Mandatory` / `Required` / `Advisory`) may sit on the head line or the following line. Anything the parser cannot read stays `category: unknown`, and never fails the run.
|
|
47
|
+
|
|
48
|
+
The `Appendix A Summary of guidelines` first line matters: `misch` passes the same file to cppcheck's `misra.py` addon, whose own parser skips everything **until** a line containing `Appendix A` and `Summary of guidelines`. Without that heading the addon loads zero rules and tags every finding with a misleading `(rule-texts-file not found: ...)` note, even though `misch`'s table still shows headlines and categories (its parser is more tolerant). Files exported for cppcheck usually carry the heading already.
|