Skip to content

longvo92/codegen-compare-tool

Repository files navigation

CodeGen Compare Tool

Test Python 3.8+ License: MIT Release

Diff two AUTOSAR code-generation output folders (MATLAB/Simulink Embedded Coder) and show only the changes that matter.

Regenerating a Simulink model rewrites timestamps, UUIDs, comment banners and auto-generated variable names even when the behaviour is identical. This tool classifies every hunk as real or ignorable, then gives you two ways to review the result: a self-contained HTML report with an AUTOSAR-level summary on top of the text diff, and a side-by-side desktop viewer with a change minimap.

Two front ends over one compare core, so a verdict never depends on how you look at it:

For Runs when
Viewer reviewing interactively: folder tree, two-pane diff, minimap no folders on the command line (or a double-clicked .exe)
CLI pipelines and scripts — writes the report, exit code gates the build both folders named on the command line

Zero dependencies for the compare itself — Python 3.8+ standard library only for the CLI and the HTML report: no pip install required, no server, no internet access. The side-by-side viewer adds PySide6, imported only when it opens.

Install

Run straight from a clone — nothing to install:

git clone https://github.com/longvo92/codegen-compare-tool.git
cd codegen-compare-tool
python -m compare_tool --help

Or install it as a command (compare-tool):

pip install git+https://github.com/longvo92/codegen-compare-tool.git

For machines where you cannot install anything, see Single-file build.

Quick start

python -m compare_tool <old_gen_folder> <new_gen_folder> [--report out.html]

The scan writes a self-contained HTML report (default compare_report.html) that opens in any browser and can be shared as a single file.

Leave the folders out and the side-by-side viewer opens instead — drop the two folders onto it:

python -m compare_tool

Exit codes:

Code Meaning
0 No real changes
1 Real changes found (useful as a CI gate)
2 Compare INCOMPLETE — some path could not be listed, read or compared (permissions, file locked by another process, long paths, …)

Exit 2 always shows: !! in the terminal, a red banner in the report. --exit-zero does not suppress it.

Command line

Flag Meaning
--report out.html Report output path (default compare_report.html). An existing file there is deleted before the scan starts
--exclude PATTERN Skip files matching a glob (relative path or bare file name), repeatable. Example: --exclude compare_report.html
--exit-zero Always exit 0 even when real changes exist (report-only mode for pipelines). Compare errors still exit 2
--arxml-only Scan only .arxml/.xml/.a2l and write a compact per-type report (default arxml_update.html) — always written, even when nothing changed
--review FILE Render notes and sign-offs from a review file (codegen-review.json, written by the viewer) next to the changes they belong to, with a Reviewed badge. Must be named explicitly; no effect with --arxml-only
--qt, --viewer Open the side-by-side viewer on folders named on the command line, instead of comparing them in the terminal. Needs the viewer extra (see below)

Omitting old_dir/new_dir opens the viewer. --gui (the tkinter panel) was removed in 1.1.0.

Side-by-side viewer

Desktop app (PySide6): folder tree, two-pane diff with a minimap, per-change review notes.

pip install "codegen-compare-tool[viewer]"   # or: pip install PySide6
python -m compare_tool                                        # then drop the folders in
python -m compare_tool --qt <old_gen_folder> <new_gen_folder> # or start loaded

Side-by-side viewer

Full walkthrough is built into the app — User guide (F1) or Release notes in the toolbar, both work offline. Standalone .exe (no Python needed): see Single-file build.

What counts as noise

Kind Rule Files
comment C comments (//, /* */), XML comments (<!-- -->) .c .h .arxml .a2l
rename Consistent 1-to-1 variable renaming (MATLAB auto-generated names). Anything the mapping can't fully explain stays a real change .c .h
uuid UUID="..." attributes .arxml .xml
timestamp <ADMIN-DATA> blocks, <DATE> .arxml .xml
sw-version <SW-VERSION> version stamps (bumped on every regenerate). Anchored, so <SW-MAJOR-VERSION> and the like are untouched .arxml .xml
whitespace Indentation, trailing spaces, blank lines all
line-endings CRLF vs LF, BOM all

Auto-generated name churn is recognised as a rename: Embedded Coder temporaries such as rtb_*, mangling suffixes and renumbered temporaries change between runs without changing behaviour.

Comment changes are their own category. A file whose differences are only comments is reported as Comment, separate from Unimportant (UUIDs, timestamps, SW-VERSION, renames, whitespace) — a rewritten comment banner triages differently from a renamed identifier. Separate counts in the CLI summary, its own tree marker and colour (purple vs yellow) in the viewer. A file mixing comments with other noise stays Unimportant. The HTML report keeps the verdict but does not display comment content — see HTML report.

Moved block detection

A block deleted in one place and reappearing intact elsewhere (Embedded Coder reorders functions and declarations when a model changes) is labelled moved and coloured blue instead of red/green. Still counts as Modified — reordering can change behaviour — it's just easier to see than two large red/green blocks.

AUTOSAR semantic summary

The tool extracts AUTOSAR information from both sides and reports changes at the semantic level, not just as text:

Source Extracted Reported
.arxml/.xml Port interfaces (SENDER-RECEIVER, CLIENT-SERVER, MODE-SWITCH, NV-DATA, PARAMETER, TRIGGER) with their full package path added / removed
.arxml/.xml SWCs (APPLICATION, SENSOR-ACTUATOR, SERVICE, CDD, ECU-ABSTRACTION, NV-BLOCK) added / removed
.arxml/.xml SWC ports (P/R/PR + referenced interface), runnables (+ SYMBOL), events (kind, PERIOD, triggered runnable) added / removed / changed (e.g. a TIMING-EVENT period going 0.01s → 0.02s, a port pointing at a different interface)
.c RTE access points — every Rte_Read/Write/Call/IrvRead/IrvWrite/Mode/Switch/… call (comments stripped before counting) added / removed
.a2l Calibration objectsCHARACTERISTIC / MEASUREMENT by name (comments and strings stripped first, so commented-out blocks do not count) added / removed

How it is shown:

  • CLI: ARXML interfaces, AUTOSAR behavior, RTE access points and A2L objects blocks listing +/-/~ entries with the file each belongs to.
  • HTML report: an AUTOSAR changes section at the top of the page, grouped by kind (port interfaces / software components / ports / runnables / events / RTE access points / A2L characteristics & measurements). Clicking a file name jumps to its detailed diff, and each file in Detailed changes carries its own Interfaces: / Behavior: / RTE: / A2L: note.
  • Whole files added or deleted contribute every interface / SWC / RTE call / A2L object inside them as added or removed.

A file whose XML fails to parse is skipped from this summary (its text diff still shows in full). An unknown Rte_ call isn't counted here but still appears in the diff.

Grouping by model / SWC

Files are grouped by Simulink model using the Embedded Coder AUTOSAR naming convention (X.c, X.h, X.arxml, Rte_X.h, the modular ARXML set, …). Files that match no model land in a final Shared / other group.

HTML report

Self-contained file, one per compare: badge toggles, folder tree, filter box, collapsible diffs per file. Opens Unimportant hidden and Modified expanded, so it opens on what matters.

Report viewer

CI integration

Run as a pipeline gate — one command, meaningful exit codes:

python -m compare_tool old_dir new_dir --exit-zero --exclude compare_report.html

--exit-zero keeps the build green on regenerated code; --exclude keeps the previous run's report from counting as a diff. Publish compare_report.html as a build artifact.

See azure-pipelines.yml for a working example (OLD checked out via git worktree, NEW is the working tree).

Single-file build

.\build.ps1           # dist\compare-tool.exe  - one file, nothing to install on the target
.\build.ps1 -Pyz      # also dist\compare_tool.pyz (~26 KB) for machines that have Python 3.8+
.\build.ps1 -PyzOnly  # zipapp only (building it needs no PyInstaller / PySide6)

dist\compare-tool.exe is one binary carrying both front ends, and it wears the tool's own icon:

Invocation What happens
compare-tool.exe <old> <new> [flags] CLI: scan, write the HTML report, exit 0/1/2
compare-tool.exe --qt <old> <new> side-by-side viewer, folders already loaded
double-click (no arguments) side-by-side viewer, waiting for the two folders

Built as a console application so terminal runs keep their exit code (1 = real changes, 2 = compare incomplete) for the CI gate. The viewer hides the console window at runtime — you'll see a brief flash on double-click. A crash un-hides the console so the error is visible.

  • .pyz (zipapp, stdlib): python compare_tool.pyz <old> <new> [flags]. Prefer it when Python is available — ~26 KB, no build dependencies, not flagged by antivirus. The CLI works anywhere; the viewer additionally needs PySide6 on that machine (without it, the tool says so instead of opening).
  • .exe (PyInstaller onefile, ~47 MB): no Python needed on the target. Building needs pyinstaller and PySide6 on the dev machine (build.ps1 installs them), and the binary only runs on the OS it was built on. PyInstaller executables are sometimes blocked by antivirus or AppLocker — fall back to the .pyz there.

Every CLI flag behaves identically in the packaged builds. build/ and dist/ are already in .gitignore.

Development

python -m unittest discover -s tests

CI runs the suite on Linux and Windows against Python 3.8 and 3.11, plus a headless scan of the fixture tree checking both the report and the exit code.

compare_tool/
├── main.py          # entry point: picks the CLI or the viewer, run_compare() core
├── resources.py     # finds the shipped icons/logo, in a checkout and in the .exe
├── qtviewer/        # PySide6 side-by-side viewer (app, diff pane, minimap, dialogs)
├── scanner.py       # walks both trees, pairs files by relative path
├── diff_engine.py   # two-pass diff (raw + normalized), hunk classification, moved-block detection
├── c_rules.py       # C/H rules: strip comments, tokenize, detect renames, extract RTE access points
├── arxml_rules.py   # ARXML rules: UUID, ADMIN-DATA, DATE, comments + extract port interfaces, SWCs (ports/runnables/events)
├── a2l_rules.py     # A2L rules: strip C-style comments + extract CHARACTERISTIC/MEASUREMENT
└── report.py        # self-contained HTML report (badge toggles, model overview, grouping, filter, collapsible diffs)

To add a rule: write the strip function in c_rules.py / arxml_rules.py, then register it in the shadow builder and _build_variants in diff_engine.py.

Issues and pull requests are welcome. Please keep the compare core stdlib-only — it has to run on locked-down build servers, so PySide6 stays confined to compare_tool/qtviewer/ and is imported only when the viewer opens — and add a test under tests/ for any new rule.

Author

Long Vo Thien

License

Released under the MIT License © 2026 Long Vo Thien.

About

Diff two AUTOSAR/Simulink codegen folders and see only the real changes — noise filtered, self-contained HTML report, zero dependencies

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages