#!/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()