#!/usr/bin/env python3 """ CLI — точка входа для запуска из командной строки. Использование: python main.py /path/to/project python main.py /path/to/project --output my_report --formats json html python main.py /path/to/project --ccn-warn 15 --ccn-critical 25 python main.py /path/to/project --libclang /usr/lib/llvm-14/lib/libclang.so.1 """ import argparse import logging import sys from pathlib import Path from config import AnalyzerConfig, ThresholdConfig from analyzer import Analyzer def build_arg_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser( prog="legacy_analyzer", description=( "Методический инструментарий модернизации унаследованных систем.\n" "Анализирует C++ и JavaScript проекты, выявляет архитектурные проблемы\n" "и рекомендует стратегию модернизации." ), formatter_class=argparse.RawDescriptionHelpFormatter, ) p.add_argument( "project_root", help="Путь к корневой директории анализируемого проекта", ) p.add_argument( "--output", "-o", default="legacy_report", help="Директория для сохранения отчётов (по умолчанию: legacy_report)", ) p.add_argument( "--formats", "-f", nargs="+", choices=["json", "html"], default=["json", "html"], help="Форматы отчётов (по умолчанию: json html)", ) p.add_argument( "--libclang", default=None, help="Путь к libclang (.so/.dylib/.dll). Если не указан — используется автоопределение или regex-fallback", ) # Пороговые значения thresholds = p.add_argument_group("Пороговые значения метрик") thresholds.add_argument("--ccn-warn", type=int, default=10, metavar="N", help="CCN порог предупреждения (по умолчанию: 10)") thresholds.add_argument("--ccn-critical", type=int, default=20, metavar="N", help="CCN критический порог (по умолчанию: 20)") thresholds.add_argument("--coupling-out-warn", type=int, default=5, metavar="N", help="Порог исходящей связности — предупреждение (по умолчанию: 5)") thresholds.add_argument("--coupling-out-critical", type=int, default=10, metavar="N", help="Порог исходящей связности — критический (по умолчанию: 10)") p.add_argument( "--verbose", "-v", action="store_true", help="Подробный вывод (DEBUG)", ) return p def main() -> None: parser = build_arg_parser() args = parser.parse_args() # --- Настройка логирования --- level = logging.DEBUG if args.verbose else logging.INFO logging.basicConfig( level=level, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%H:%M:%S", ) # --- Проверка входного пути --- root = Path(args.project_root) if not root.exists(): print(f"Ошибка: директория не найдена: {root}", file=sys.stderr) sys.exit(1) # --- Сборка конфигурации --- thresholds = ThresholdConfig( ccn_warn=args.ccn_warn, ccn_critical=args.ccn_critical, coupling_out_warn=args.coupling_out_warn, coupling_out_critical=args.coupling_out_critical, ) config = AnalyzerConfig( output_formats=args.formats, output_dir=args.output, libclang_path=args.libclang, thresholds=thresholds, ) # --- Запуск --- try: analyzer = Analyzer(config) report = analyzer.run(str(root)) # Краткий итог в stdout print("\n" + "=" * 50) print(f" Системный риск: {report.system_risk_level.name}") print(f" Стратегия: {report.recommended_system_strategy.name}") print(f" Модулей: {len(report.modules)}") print(f" Функций: {report.total_functions}") print(f" Средняя CCN: {report.avg_system_ccn:.2f}") print(f" Циклов зависим.: {len(report.dependency_cycles)}") print(f" Отчёты в: {args.output}/") print("=" * 50) except RuntimeError as exc: print(f"Ошибка: {exc}", file=sys.stderr) sys.exit(1) except KeyboardInterrupt: print("\nПрервано пользователем", file=sys.stderr) sys.exit(130) if __name__ == "__main__": main()