Files
qt-migration-legatus/add_logger_init.py
T

247 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Add logger initialization to constructors in Qt6 .cpp files.
Pattern: m_logger(NGL::NGLLoggerBootstrap::getLogger())
"""
import os
import re
import sys
from pathlib import Path
# Paths
SCRIPT_DIR = Path(__file__).parent
QT6_BASE = SCRIPT_DIR / "qt6-after/qt6-migrated-legatus"
# Qt classes that shouldn't have logger initialization
QT_CLASSES = {
'QObject', 'QWidget', 'QMainWindow', 'QDialog', 'QMessageBox',
'QAbstractTableModel', 'QAbstractListModel', 'QAbstractItemModel',
'QTreeView', 'QTableView', 'QListView', 'QComboBox',
'QTcpSocket', 'QTcpServer', 'QUdpSocket', 'QTimer', 'QThread',
'QSettings', 'QFileInfo', 'QDir', 'QFile', 'QTextStream',
'QDomElement', 'QDomDocument', 'QXmlStreamReader', 'QXmlStreamWriter',
'QPainter', 'QPixmap', 'QImage', 'QIcon', 'QBrush', 'QPen',
'QMenu', 'QMenuBar', 'QToolBar', 'QStatusBar', 'QAction',
'QVBoxLayout', 'QHBoxLayout', 'QGridLayout', 'QFormLayout',
'QPushButton', 'QLabel', 'QLineEdit', 'QTextEdit', 'QPlainTextEdit',
'QCheckBox', 'QRadioButton', 'QSpinBox', 'QDoubleSpinBox',
'QSlider', 'QScrollBar', 'QProgressBar', 'QTabWidget',
'QStackedWidget', 'QSplitter', 'QScrollArea', 'QFrame',
'QGroupBox', 'QToolBox', 'QWizard', 'QFontDialog', 'QColorDialog',
'QFileDialog', 'QInputDialog', 'QProgressDialog', 'QErrorMessage',
'QSystemTrayIcon', 'QClipboard', 'QDrag', 'QMimeData',
'QKeyEvent', 'QMouseEvent', 'QPaintEvent', 'QResizeEvent',
'QShowEvent', 'QHideEvent', 'QCloseEvent', 'QFocusEvent',
'QMoveEvent', 'QContextMenuEvent', 'QInputEvent', 'QWheelEvent',
'QHoverEvent', 'QTabletEvent', 'QGestureEvent', 'QShortcut',
'QKeySequence', 'QValidator', 'QIntValidator', 'QDoubleValidator',
'QRegExpValidator', 'QCompleter', 'QAbstractSpinBox', 'QDateTimeEdit',
'QDateEdit', 'QTimeEdit', 'QCalendarWidget', 'QDial', 'QLCDNumber',
'QSpinBox', 'QDoubleSpinBox', 'QTimeEdit', 'QDateTimeEdit',
'QDateEdit', 'QFontComboBox', 'QToolButton', 'QCommandLinkButton',
'QDialogButtonBox', 'QPlainTextEdit', 'QTextEdit', 'QTextBrowser',
'QListWidget', 'QTreeWidget', 'QTableWidget', 'QListWidgetItem',
'QTreeWidgetItem', 'QTableWidgetItem', 'QHeaderView', 'QColumnView',
'QDataWidgetMapper', 'QFileSystemModel', 'QStandardItemModel',
'QStandardItem', 'QSortFilterProxyModel', 'QStringListModel',
'QAbstractProxyModel', 'QIdentityProxyModel', 'QItemSelectionModel',
'QItemSelection', 'QItemSelectionRange', 'QStyledItemDelegate',
'QItemDelegate', 'QAbstractItemDelegate', 'QAbstractItemView',
'QGraphicsView', 'QGraphicsScene', 'QGraphicsItem', 'QGraphicsWidget',
'QGraphicsObject', 'QGraphicsLayout', 'QGraphicsLinearLayout',
'QGraphicsGridLayout', 'QGraphicsProxyWidget', 'QGraphicsTextItem',
'QGraphicsRectItem', 'QGraphicsEllipseItem', 'QGraphicsLineItem',
'QGraphicsPathItem', 'QGraphicsPolygonItem', 'QGraphicsPixmapItem',
'QGraphicsSimpleTextItem', 'QGraphicsItemGroup', 'QGraphicsLayoutItem',
'QGraphicsAnchorLayout', 'QGraphicsGridLayout', 'QStackedLayout',
'QBoxLayout', 'QFormLayout', 'QGridLayout', 'QHBoxLayout',
'QVBoxLayout', 'QLayout', 'QLayoutItem', 'QSpacerItem',
'QSizeGrip', 'QSizePolicy', 'QStyle', 'QStyleFactory',
'QStyleOption', 'QStylePainter', 'QProxyStyle', 'QCommonStyle',
'QWindowsVistaStyle', 'QMacStyle', 'QFusionStyle', 'QCleanlooksStyle',
'QPlastiqueStyle', 'QMotifStyle', 'QApplication', 'QDesktopWidget',
'QSessionManager', 'QStyleHintReturn', 'QStyleHintReturnMask',
'QStyleHintReturnVariant', 'QStyleOptionFocusRect',
'QStyleOptionFrame', 'QStyleOptionFrameV2', 'QStyleOptionFrameV3',
'QStyleOptionButton', 'QStyleOptionComboBox', 'QStyleOptionHeader',
'QStyleOptionHeaderV2', 'QStyleOptionComplex', 'QStyleOptionSlider',
'QStyleOptionSpinBox', 'QStyleOptionToolButton', 'QStyleOptionToolBar',
'QStyleOptionDockWidget', 'QStyleOptionDockWidgetV2',
'QStyleOptionViewItem', 'QStyleOptionViewItemV2', 'QStyleOptionViewItemV3',
'QStyleOptionViewItemV4', 'QStyleOptionTab', 'QStyleOptionTabV2',
'QStyleOptionTabV3', 'QStyleOptionTabBarBase', 'QStyleOptionTabBarBaseV2',
'QStyleOptionRubberBand', 'QStyleOptionGraphicsItem',
'QStyleOptionTitleBar', 'QStyleOptionToolBox', 'QStyleOptionToolBoxV2',
'QStyleOptionViewItem', 'QStyleOptionProgressBar',
'QStyleOptionProgressBarV2', 'QStyleOptionMenuItem', 'QStyleOptionSlider',
'QStyleOptionSpinBox', 'QStyleOptionToolButton', 'QStyleOptionToolBar',
'QStyleOptionDockWidget', 'QStyleOptionDockWidgetV2',
'QStyleOptionFocusRect', 'QStyleOptionFrame', 'QStyleOptionFrameV2',
'QStyleOptionFrameV3', 'QStyleOptionButton', 'QStyleOptionComboBox',
'QStyleOptionHeader', 'QStyleOptionHeaderV2', 'QStyleOptionComplex',
'QStyleOptionTab', 'QStyleOptionTabV2', 'QStyleOptionTabV3',
'QStyleOptionTabBarBase', 'QStyleOptionTabBarBaseV2',
'QStyleOptionRubberBand', 'QStyleOptionGraphicsItem',
'QStyleOptionTitleBar', 'QStyleOptionToolBox', 'QStyleOptionToolBoxV2',
'QStyleOptionProgressBar', 'QStyleOptionProgressBarV2',
'QStyleOptionMenuItem', 'QStyleOptionSizeMode', 'QStyleOptionVersion',
}
def find_cpp_files_with_logger_member():
"""Find all Qt6 .cpp files that have m_logger member in corresponding header"""
cpp_files = []
for cpp_file in QT6_BASE.rglob("*.cpp"):
# Find corresponding header
header = cpp_file.with_suffix('.h')
if not header.exists():
header = cpp_file.with_suffix('.hpp')
if header.exists():
try:
header_content = header.read_text(encoding='utf-8', errors='ignore')
if 'm_logger' in header_content:
cpp_files.append(cpp_file)
except:
pass
print(f"Found {len(cpp_files)} .cpp files with m_logger in header")
return cpp_files
def add_logger_init_to_constructor(content, class_name):
"""Add logger initialization to constructor"""
# Pattern to match constructor initialization list
# Matches: ClassName::ClassName(...) : existing_init1(val1), existing_init2(val2)
# Also matches: ClassName::ClassName(...) {
# And: ClassName::ClassName(...)
# First check if already has logger init
if 'm_logger(' in content or 'm_logger(NGL' in content:
return content, False, "Already initialized"
# Pattern for constructor with initialization list
# Group 1: constructor signature up to :
# Group 2: existing initializers
# Group 3: opening brace or colon continuing init list
# Try to find constructor with init list
pattern_with_init = rf'({re.escape(class_name)}::\s*{re.escape(class_name)}\s*\([^)]*\))\s*:\s*([^{{]+?)\s*({{)'
match = re.search(pattern_with_init, content, re.MULTILINE | re.DOTALL)
if match:
# Add m_logger to existing init list
before = match.group(1)
inits = match.group(2).rstrip()
brace = match.group(3)
# Add m_logger initialization (comma if needed)
new_init = f"{before} : {inits},\n m_logger(NGL::NGLLoggerBootstrap::getLogger()) {brace}"
new_content = content[:match.start()] + new_init + content[match.end():]
return new_content, True, "Added to init list"
# Try constructor without init list (direct brace)
pattern_direct_brace = rf'({re.escape(class_name)}::\s*{re.escape(class_name)}\s*\([^)]*\))\s*({{)'
match = re.search(pattern_direct_brace, content, re.MULTILINE)
if match:
sig = match.group(1)
brace = match.group(2)
new_constructor = f"{sig} : m_logger(NGL::NGLLoggerBootstrap::getLogger()) {brace}"
new_content = content[:match.start()] + new_constructor + content[match.end():]
return new_content, True, "Added init list"
# Try constructor that continues on next line
pattern_multiline = rf'({re.escape(class_name)}::\s*{re.escape(class_name)}\s*\([^)]*\))\s*\n\s*:\s*([^{{]+?)\s*({{)'
match = re.search(pattern_multiline, content, re.MULTILINE)
if match:
before = match.group(1)
inits = match.group(2).rstrip()
brace = match.group(3)
new_init = f"{before}\n : {inits},\n m_logger(NGL::NGLLoggerBootstrap::getLogger()) {brace}"
new_content = content[:match.start()] + new_init + content[match.end():]
return new_content, True, "Added to multiline init"
return content, False, "No constructor found"
def extract_class_name_from_header(header_path):
"""Extract class name from header file"""
try:
content = header_path.read_text(encoding='utf-8', errors='ignore')
# Find first class definition (not forward declaration)
pattern = r'^class\s+(\w+)\s*(?::\s*public\s+\w+)?\s*\{'
match = re.search(pattern, content, re.MULTILINE)
if match:
return match.group(1)
except:
pass
return None
def main():
print("=" * 80)
print("Adding logger initialization to constructors")
print("=" * 80)
# Find cpp files with m_logger in header
cpp_files = find_cpp_files_with_logger_member()
# Track results
added = 0
skipped = 0
skip_reasons = {}
examples = []
for cpp_file in cpp_files:
try:
content = cpp_file.read_text(encoding='utf-8')
# Get class name from header
header = cpp_file.with_suffix('.h')
if not header.exists():
header = cpp_file.with_suffix('.hpp')
class_name = None
if header.exists():
class_name = extract_class_name_from_header(header)
if not class_name:
# Try to guess from filename
class_name = cpp_file.stem
# Try to add logger init
new_content, success, message = add_logger_init_to_constructor(content, class_name)
if success:
cpp_file.write_text(new_content, encoding='utf-8')
added += 1
if added <= 10:
print(f"ADDED: {cpp_file.relative_to(QT6_BASE)} - {message}")
else:
skipped += 1
reason = message
skip_reasons[reason] = skip_reasons.get(reason, 0) + 1
if len(examples) < 5:
examples.append(f"{cpp_file.relative_to(QT6_BASE)} ({class_name})")
except Exception as e:
skipped += 1
reason = f"Error: {str(e)}"
skip_reasons[reason] = skip_reasons.get(reason, 0) + 1
print("\n" + "=" * 80)
print(f"Results: {added} files updated, {skipped} skipped")
print("\nSkip reasons:")
for reason, count in sorted(skip_reasons.items(), key=lambda x: -x[1])[:10]:
print(f" {reason}: {count}")
if examples:
print("\nSkipped examples (first 5):")
for example in examples:
print(f" - {example}")
print("=" * 80)
if __name__ == "__main__":
main()