132 lines
5.0 KiB
Python
132 lines
5.0 KiB
Python
#!/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()
|