Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
All notable changes to this project are documented here. Versions follow
[semantic versioning](https://semver.org/).

## [Unreleased]

## [1.17.0] — 2026-09-27

### Changed

- Improve terminal-only comparisons with built-in diagnostics and concise per-file summaries.

## [1.16.0] — 2026-09-15

### Added
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Print only a terminal summary:
python -m compare_tool baseline current --no-report
```

The terminal mode shows a per-model Overview, every file and its verdict, AUTOSAR/A2L changes, and consistency warnings. It does not print source diffs or create HTML.
The terminal mode shows a per-model Overview, AUTOSAR/A2L changes, consistency warnings, effective compare settings, hunk-classification totals and one summary for each non-identical file. It does not print individual hunks, source diffs or create HTML.

Open the desktop viewer:

Expand All @@ -62,7 +62,7 @@ Folders and ZIP archives are accepted as inputs.
|---|---|
| Desktop viewer | Interactive side-by-side review |
| HTML report | Sharing or publishing as a CI artifact |
| `--no-report` | Fast terminal review without generated files |
| `--no-report` | Detailed terminal review and diagnostics without generated files |
| `--json` | Structured pipeline processing |
| `--sarif` | Code-scanning annotations |

Expand Down
2 changes: 1 addition & 1 deletion compare_tool/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""CodeGen Compare Tool - AUTOSAR MATLAB codegen diff with noise filtering."""

__version__ = "1.16.0"
__version__ = "1.17.0"
170 changes: 124 additions & 46 deletions compare_tool/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
"""

import argparse
import platform
import shutil
import sys
import tempfile
from collections import Counter
from pathlib import Path

from . import __version__, review, serialize, theme, userrules, zipsource
from .diff_engine import RULES
from .diff_engine import RULES, ruleset_for
from .report import (build_arxml_report, build_report, consistency_advisories,
model_overview)
from .view_model import SWC_DISPLAY, iface_kind, swc_item
Expand Down Expand Up @@ -103,36 +105,6 @@ def run_compare(old_root, new_root, out, arxml_only=False, exclude=(),
return results, counts


def _terminal_tree_lines(results):
"""ASCII folder tree with a verdict for every scanned path."""
root = {}
for rel in sorted(results):
parts = rel.replace('\\', '/').split('/')
node = root
for part in parts[:-1]:
node = node.setdefault(part + '/', {})
node[parts[-1]] = results[rel]['status']
lines = ['Folder tree:']

def walk(node, prefix):
entries = sorted(node, key=lambda name: (not name.endswith('/'), name))
for index, name in enumerate(entries):
last = index == len(entries) - 1
branch = '`-- ' if last else '|-- '
value = node[name]
if isinstance(value, dict):
lines.append(prefix + branch + name)
walk(value, prefix + (' ' if last else '| '))
else:
label = 'modified' if value == 'real-change' else value
lines.append('{}{}{} [{}]'.format(prefix, branch, name, label))

walk(root, '')
if not root:
lines.append(' (no files matched)')
return lines


def _terminal_overview_lines(results):
"""Aligned per-model Overview using the report's renderer-neutral rows."""
rows = model_overview(results)
Expand Down Expand Up @@ -170,12 +142,12 @@ def autosar_text(parts):
return lines


def summary_lines(results, counts, tree=False):
def summary_lines(results, counts, terminal=False):
"""Scan summary as plain-text lines the CLI prints: counts, uncompared
paths, modified files and the AUTOSAR/A2L semantic rollups.

``tree=True`` replaces modified-file hunk counts with every scanned path
and its verdict. Error and consistency warnings are shared by both modes.
``terminal=True`` adds the per-model Overview and a semantic-section
heading for ``--no-report``. Per-file diagnostics are rendered separately.
"""
lines = []
lines.append('Summary: {real-change} modified, {comment-only} comment-only, '
Expand All @@ -196,14 +168,12 @@ def summary_lines(results, counts, tree=False):
if r['status'] == 'error':
for note in r['notes']:
lines.append(' !! {} -- {}'.format(rel, note))
if tree:
lines.append('')
if terminal:
overview = _terminal_overview_lines(results)
if overview:
lines.extend(overview)
lines.append('')
lines.extend(_terminal_tree_lines(results))
modified_files = () if tree else sorted(results.items())
lines.extend(overview)
modified_files = () if terminal else sorted(results.items())
for rel, r in modified_files:
if r['status'] == 'real-change':
n_real = sum(1 for h in r['hunks'] if h['kind'] == 'real')
Expand All @@ -213,7 +183,7 @@ def summary_lines(results, counts, tree=False):
lines.append(' MODIFIED {} ({} hunk(s){})'.format(
rel, n_real, ', {} moved'.format(n_moved) if n_moved else ''))

if tree:
if terminal:
lines.append('')
lines.append('AUTOSAR / A2L changes:')
semantic_start = len(lines)
Expand Down Expand Up @@ -266,7 +236,7 @@ def summary_lines(results, counts, tree=False):
for rel, n, kind in a2l_removed:
lines.append(' - {} ({}) in {}'.format(n, kind, rel))

if tree and len(lines) == semantic_start:
if terminal and len(lines) == semantic_start:
lines.append(' No extracted AUTOSAR/A2L changes.')

# cross-artifact heads-up: a model whose ARXML and C did not change
Expand All @@ -279,6 +249,105 @@ def summary_lines(results, counts, tree=False):
return lines


_HUNK_KIND_ORDER = (
'real', 'moved', 'comment', 'rename', 'assumed-rename', 'reorder',
'uuid', 'timestamp', 'sw-version', 'description', 'whitespace', 'mixed',
)


def diagnostic_lines(results, arxml_only=False, exclude=(), user_rules=(),
skip_var_renames=False, source_kinds=('folder', 'folder')):
"""Technical terminal trace included by ``--no-report``.

The trace explains the effective compare settings, aggregate hunk
classifications and one summary line per non-identical path without
printing source content. It is deliberately derived from the raw scan
result so it cannot change a verdict, count or exit code.
"""
hunk_counts = Counter(
h.get('kind', 'unknown')
for result in results.values()
for h in result.get('hunks', ())
)
differences = sum(
result['status'] not in ('identical', 'error')
for result in results.values()
)
errors = sum(result['status'] == 'error' for result in results.values())
binary_changes = sum(
bool(result.get('binary')) for result in results.values()
)
rule_names = [rule.name for rule in user_rules]

lines = [
'',
'Diagnostics:',
' Runtime: codegen-compare-tool {} | Python {} | {}'.format(
__version__, platform.python_version(), sys.platform),
' Sources: BASELINE {}; CURRENT {}'.format(*source_kinds),
' Scope: {}'.format(
'ARXML/XML/A2L only' if arxml_only else 'all matched files'),
' Excludes: {}'.format(', '.join(exclude) if exclude else 'none'),
' Custom noise rules: {}'.format(
', '.join(rule_names) if rule_names else 'none'),
' Unsafe rename quick check: {}'.format(
'enabled' if skip_var_renames else 'disabled'),
' Paths: {} scanned; {} with differences; {} not compared; '
'{} binary change(s)'.format(
len(results), differences, errors, binary_changes),
]

lines.append('Hunk classification: {} total'.format(sum(hunk_counts.values())))
if hunk_counts:
order = {kind: index for index, kind in enumerate(_HUNK_KIND_ORDER)}
for kind, count in sorted(
hunk_counts.items(),
key=lambda item: (order.get(item[0], len(order)), item[0])):
lines.append(' {}: {}'.format(kind, count))
else:
lines.append(' (no text hunks; one-sided, binary or identical files only)')

order = {kind: index for index, kind in enumerate(_HUNK_KIND_ORDER)}

lines.append('File summary:')
traced = [(rel, result) for rel, result in sorted(results.items())
if result['status'] != 'identical']
if not traced:
lines.append(' (no differences or compare errors)')
return lines

for rel, result in traced:
hunks = result.get('hunks', ())
content = 'binary' if result.get('binary') else 'text'
per_file = Counter(h.get('kind', 'unknown') for h in hunks)
hunk_summary = ', '.join(
'{}={}'.format(kind, count)
for kind, count in sorted(
per_file.items(),
key=lambda item: (order.get(item[0], len(order)), item[0])))
details = [
'ruleset={}'.format(ruleset_for(rel)),
content,
'{} hunk(s){}'.format(
len(hunks), ': ' + hunk_summary if hunk_summary else ''),
]
if result.get('renames'):
details.append('{} rename(s)'.format(len(result['renames'])))
if result.get('moved_from'):
details.append('move from BASELINE {} ({:.1%}, {})'.format(
result['moved_from'], result.get('move_similarity', 0),
result.get('move_status', 'unknown')))
if result.get('moved_to'):
details.append('move to CURRENT {} ({:.1%}, {})'.format(
result['moved_to'], result.get('move_similarity', 0),
result.get('move_status', 'unknown')))
if result.get('notes'):
details.append('notes: {}'.format('; '.join(result['notes'])))
lines.append(' {} [{}] {}'.format(
rel, result['status'], '; '.join(details)))
return lines


def _parser():
ap = argparse.ArgumentParser(
prog='compare_tool',
Expand All @@ -304,10 +373,11 @@ def _parser():
help='HTML report output path (default: compare_report.html, '
'or arxml_update.html with --arxml-only)')
reports.add_argument('--no-report', action='store_true',
help='terminal summary only: show every scanned file '
'in a folder tree with its verdict, plus AUTOSAR '
'and A2L changes, without code diffs or an HTML '
'report. Requires both input paths; existing '
help='terminal summary and diagnostics only: show the '
'model Overview, AUTOSAR/A2L changes, effective '
'settings, hunk-kind totals and one summary per '
'non-identical file, without source diffs or an '
'HTML report. Requires both input paths; existing '
'reports are left untouched')
ap.add_argument('--arxml-only', action='store_true',
help='compare only ARXML/XML and A2L files and write a '
Expand Down Expand Up @@ -566,8 +636,16 @@ def progress(done, total, rel):
# normal outcome, and a run that produced no report must not be
# indistinguishable from it (--exit-zero cannot mask this either)
return 2
for line in summary_lines(results, counts, tree=args.no_report):
for line in summary_lines(results, counts, terminal=args.no_report):
print(line)
if args.no_report:
for line in diagnostic_lines(
results, arxml_only=args.arxml_only, exclude=args.exclude,
user_rules=user_rules,
skip_var_renames=args.skip_var_renames,
source_kinds=('zip' if old_zip else 'folder',
'zip' if new_zip else 'folder')):
print(line)

if out is not None and args.arxml_only:
if counts['real-change'] or counts['added'] or counts['deleted']:
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Do not copy mappings or rollup logic into CLI, HTML or Qt code.

`run_compare` removes a stale target report before scanning. A report-write failure preserves the scan for terminal output and exits `2`.

With `--no-report`, `run_compare` skips HTML rendering. `summary_lines(..., tree=True)` prints the Overview, full tree, semantic details and warnings without source hunks. `report.model_overview` supplies the same structured rows to HTML and terminal renderers.
With `--no-report`, `run_compare` skips HTML rendering. `summary_lines(..., terminal=True)` prints the Overview, semantic details and warnings without source hunks. `diagnostic_lines` adds effective settings, aggregate hunk-kind totals and one summary per non-identical file. Both consume the raw result and never change verdicts, counts or exit codes. `report.model_overview` supplies the same structured rows to HTML and terminal renderers.

### Viewer

Expand Down
10 changes: 7 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ This mode creates no HTML and prints no source-code hunks. It prints:

1. total verdict counts;
2. a per-model Overview with file counts and AUTOSAR changes;
3. a complete folder tree with one verdict per file;
4. detailed AUTOSAR/A2L changes;
5. consistency and quick-check warnings.
3. detailed AUTOSAR/A2L changes;
4. consistency and quick-check warnings;
5. tool/Python/platform versions and effective compare options;
6. totals for each hunk classification;
7. one summary per non-identical file with its verdict, ruleset, hunk-kind counts, rename/move totals and error notes.

An existing report is left untouched. `--json` and `--sarif` still write their requested files.

The diagnostics do not print source content and do not change verdicts or exit codes.

### Common options

| Option | Purpose |
Expand Down
2 changes: 1 addition & 1 deletion docs/vi/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Không copy mapping hoặc rollup logic vào CLI, HTML hay Qt renderer.

`main.viewer_requested` chọn Qt hoặc terminal comparison. `run_compare` xóa stale report trước khi scan; lỗi ghi report vẫn in được scan result và trả exit code `2`.

Với `--no-report`, tool bỏ qua HTML rendering. `summary_lines(..., tree=True)` in Overview, full tree, semantic details và warnings. `report.model_overview` cấp cùng structured rows cho HTML và terminal.
Với `--no-report`, tool bỏ qua HTML rendering. `summary_lines(..., terminal=True)` in Overview, semantic details và warnings mà không in source hunk. `diagnostic_lines` bổ sung effective settings, tổng số hunk theo kind và một summary cho mỗi file không identical. Cả hai đều dùng raw result và không thay đổi verdict, count hoặc exit code. `report.model_overview` cấp cùng structured rows cho HTML và terminal.

Viewer chạy scanner trong `qtviewer/worker.py` bằng `QThread`. Export report luôn dùng raw scan, không dùng filtered tree.

Expand Down
10 changes: 7 additions & 3 deletions docs/vi/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ Mode này không tạo HTML và không in source-code hunk. Terminal hiển th

1. tổng số file theo verdict;
2. Overview theo model với file count và AUTOSAR changes;
3. folder tree đầy đủ với verdict của từng file;
4. AUTOSAR/A2L changes chi tiết;
5. consistency và quick-check warnings.
3. AUTOSAR/A2L changes chi tiết;
4. consistency và quick-check warnings;
5. version của tool/Python/platform và các compare option thực tế;
6. tổng số theo từng hunk classification;
7. một summary cho mỗi file không identical gồm verdict, ruleset, số hunk theo kind, tổng rename/move và error note.

Report đã có từ trước không bị thay đổi. `--json` và `--sarif` vẫn tạo file khi được chỉ định.

Diagnostics không in source content và không thay đổi verdict hoặc exit code.

### Options thường dùng

| Option | Công dụng |
Expand Down
Loading
Loading