- HandlerMinelayerData: BUILD_RTI Guards für RTI-Abhängigkeiten - svcTabCommon: Export-Header-Fix via Fallback-Target - TrainingAreaBase: Stub-Header für BUILD_RTI=OFF - ddsfts: Include-Pfad und Dependency-Fixes - BUILD_RTI=OFF Compilation vollständig unterstützt Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Entire-Checkpoint: 01M0JNZA586SACX1PRZ2DJAE5S
356 lines
13 KiB
Python
356 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Export Header Fix Script v5
|
|
|
|
Repariert falsche Export-Header-Includes in Qt6-migrierten Dateien.
|
|
|
|
WICHTIG:
|
|
- Nur SHARED/MODULE Libraries bekommen Export-Header
|
|
- STATIC Libraries brauchen KEINE Export-Header
|
|
- Export-Header aus Build-Verzeichnis benötigen "..." (doppelte Anführungszeichen)
|
|
- Nur Files im gleichen Verzeichnis wie CMakeLists.txt (oder Subverzeichnissen) werden verarbeitet
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
def parse_cmakelists(cmakelists_path):
|
|
"""
|
|
Parse CMakeLists.txt und extrahiere:
|
|
- SET() Variablen mit ihren Files
|
|
- NGL_ADD_LIBRARY() Aufrufe mit Target-Name, Typ und Sources-Variable
|
|
"""
|
|
with open(cmakelists_path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
content = f.read()
|
|
|
|
# SET() Variablen extrahieren: SET(VARNAME file1.cpp file2.h ...)
|
|
set_vars = {}
|
|
set_pattern = r'SET\s*\(\s*(SRCS\w*)\s*((?:[^)]+))\)'
|
|
for match in re.finditer(set_pattern, content, re.IGNORECASE):
|
|
var_name = match.group(1).strip()
|
|
# Files extrahieren (alles nach VARNAME bis zum schließenden Parenthesis)
|
|
files_str = match.group(2).strip()
|
|
# Neueinlines und multiple spaces zu single space
|
|
files_str = ' '.join(files_str.split())
|
|
# Files extrahieren (Wörter die mit . enden oder .extension haben)
|
|
files = re.findall(r'[\w\.\-_/]+', files_str)
|
|
# Filtere nur Header/Source Files
|
|
files = [f for f in files if any(f.endswith(ext) for ext in ['.h', '.hpp', '.cpp', '.cc', '.cxx'])]
|
|
set_vars[var_name] = files
|
|
|
|
# NGL_ADD_LIBRARY() Aufrufe extrahieren
|
|
# Pattern: NGL_ADD_LIBRARY(... target_name SHARED|STATIC|MODULE ${SRCS_var} ...)
|
|
libraries = []
|
|
|
|
# Multi-line Pattern für NGL_ADD_LIBRARY
|
|
lib_pattern = r'NGL_ADD_LIBRARY\s*\(([^)]+)\)'
|
|
for match in re.finditer(lib_pattern, content, re.IGNORECASE | re.DOTALL):
|
|
args_str = match.group(1).strip()
|
|
args = ' '.join(args_str.split()) # Whitespace normalisieren
|
|
|
|
# Target-Typ finden (SHARED, STATIC, MODULE)
|
|
lib_type = None
|
|
if re.search(r'\bSHARED\b', args, re.IGNORECASE):
|
|
lib_type = 'SHARED'
|
|
elif re.search(r'\bMODULE\b', args, re.IGNORECASE):
|
|
lib_type = 'MODULE'
|
|
elif re.search(r'\bSTATIC\b', args, re.IGNORECASE):
|
|
lib_type = 'STATIC'
|
|
|
|
if not lib_type:
|
|
continue
|
|
|
|
# Target-Namen finden (nach den Platform-Flags wie AAR, SV, WS, HLA, ALL)
|
|
# Pattern: NGL_ADD_LIBRARY(platforms... target_name TYPE ...)
|
|
tokens = args.split()
|
|
target_name = None
|
|
src_var = None
|
|
|
|
# Nach Platform-Flags suchen und dann den nächsten Token als Target-Name nehmen
|
|
platform_flags = {'AAR', 'SV', 'WS', 'HLA', 'ALL', 'WIN', 'LINUX'}
|
|
found_platform = False
|
|
for i, token in enumerate(tokens):
|
|
token_upper = token.upper()
|
|
if token_upper in platform_flags:
|
|
found_platform = True
|
|
continue
|
|
if found_platform and target_name is None:
|
|
# Nächster Token nach Platform-Flags ist der Target-Name
|
|
if token_upper not in {'SHARED', 'STATIC', 'MODULE'}:
|
|
target_name = token
|
|
continue
|
|
if target_name and token_upper in {'SHARED', 'STATIC', 'MODULE'}:
|
|
# Nach Target-Name und Typ suchen wir nach ${SRCS_var}
|
|
for j in range(i+1, min(i+5, len(tokens))):
|
|
if tokens[j].startswith('${') and tokens[j].endswith('}'):
|
|
var_name = tokens[j][2:-1] # ${VAR} -> VAR
|
|
if var_name in set_vars:
|
|
src_var = var_name
|
|
break
|
|
break
|
|
|
|
if target_name:
|
|
libraries.append({
|
|
'name': target_name,
|
|
'type': lib_type,
|
|
'src_var': src_var,
|
|
'files': set_vars.get(src_var, []) if src_var else []
|
|
})
|
|
|
|
return libraries
|
|
|
|
def find_cmakelists_for_file(header_path, base_dir):
|
|
"""Finde CMakeLists.txt und prüfe alle Parent-Verzeichnisse"""
|
|
# Suche aufsteigend nach CMakeLists.txt
|
|
current = header_path.parent
|
|
while current != base_dir and current != base_dir.parent:
|
|
cmakelists = current / 'CMakeLists.txt'
|
|
if cmakelists.exists():
|
|
return cmakelists
|
|
current = current.parent
|
|
|
|
return None
|
|
|
|
def search_file_in_cmakelists(header_path, cmakelists):
|
|
"""Suche ob ein File in einem CMakeLists.txt referenziert wird"""
|
|
cmakelists_dir = cmakelists.parent
|
|
|
|
# Versuche relativen Pfad vom CMakeLists.txt Verzeichnis
|
|
try:
|
|
rel_path = header_path.relative_to(cmakelists_dir)
|
|
rel_path_str = str(rel_path)
|
|
file_name = header_path.name
|
|
except ValueError:
|
|
return None, None
|
|
|
|
libraries = parse_cmakelists(cmakelists)
|
|
|
|
for lib in libraries:
|
|
# Prüfe ob File in den Sources ist
|
|
for src_file in lib['files']:
|
|
if src_file == rel_path_str or src_file == file_name:
|
|
return lib['name'], lib['type']
|
|
|
|
return None, None
|
|
|
|
def get_export_header_name(target_name):
|
|
"""Generiere Export-Header Name aus Target-Name"""
|
|
return f"{target_name.lower()}_export.h"
|
|
|
|
def fix_export_header_in_file(header_path, target_name, lib_type):
|
|
"""
|
|
Repariere Export-Header Include in einer Datei.
|
|
Return: (fixed, old_include, new_include) oder (False, None, None)
|
|
"""
|
|
with open(header_path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
content = f.read()
|
|
|
|
# Finde bestehende Export-Header Includes
|
|
# Pattern: #include "xxx_export.h" oder #include <xxx_export.h>
|
|
include_pattern = r'#\s*include\s*[<"]([^>"\s]+_export\.h)[>"]'
|
|
matches = list(re.finditer(include_pattern, content))
|
|
|
|
if not matches:
|
|
return False, None, None
|
|
|
|
expected_export = get_export_header_name(target_name)
|
|
|
|
# Prüfe alle Export-Header Includes
|
|
fixed = False
|
|
old_include = None
|
|
new_include = None
|
|
|
|
for match in matches:
|
|
current_export = match.group(1)
|
|
|
|
# Wenn bereits korrekt, skip
|
|
if current_export == expected_export:
|
|
# Prüfe ob es "..." statt <...> verwendet (für Build-Verzeichnis)
|
|
full_match = match.group(0)
|
|
if full_match.startswith('#include <'):
|
|
# Falsche Syntax, sollte "..." sein
|
|
new_include = f'#include "{expected_export}"'
|
|
old_include = full_match
|
|
content = content[:match.start()] + new_include + content[match.end():]
|
|
fixed = True
|
|
continue
|
|
|
|
# Falscher Export-Header
|
|
old_include = f'#include "{current_export}"' if f'#include "{current_export}"' in content else f'#include <{current_export}>'
|
|
new_include = f'#include "{expected_export}"'
|
|
content = content.replace(old_include, new_include, 1)
|
|
fixed = True
|
|
break
|
|
|
|
if fixed:
|
|
with open(header_path, 'w', encoding='utf-8') as f:
|
|
f.write(content)
|
|
|
|
return fixed, old_include, new_include
|
|
|
|
def remove_export_header_from_static(header_path):
|
|
"""Entferne Export-Header Include aus STATIC Library File"""
|
|
with open(header_path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
content = f.read()
|
|
|
|
# Finde Export-Header Includes
|
|
include_pattern = r'#\s*include\s*[<"]([^>"\s]+_export\.h)[>"]'
|
|
matches = list(re.finditer(include_pattern, content))
|
|
|
|
if not matches:
|
|
return False, None
|
|
|
|
old_include = matches[0].group(0)
|
|
content = content.replace(old_include, '', 1)
|
|
# Doppelte Leerzeilen entfernen
|
|
content = re.sub(r'\n\s*\n\s*\n', '\n\n', content)
|
|
|
|
with open(header_path, 'w', encoding='utf-8') as f:
|
|
f.write(content)
|
|
|
|
return True, old_include
|
|
|
|
def main():
|
|
if len(sys.argv) != 2:
|
|
print("Usage: fix_export_headers_v5.py <base_dir>")
|
|
sys.exit(1)
|
|
|
|
base_dir = Path(sys.argv[1]).resolve()
|
|
|
|
# Finde alle CMakeLists.txt Dateien
|
|
cmakelists_files = list(base_dir.rglob('CMakeLists.txt'))
|
|
print(f"Found {len(cmakelists_files)} CMakeLists.txt files")
|
|
|
|
# Parse alle CMakeLists.txt und baue Mapping: file_path -> (target_name, lib_type)
|
|
file_to_target = {}
|
|
|
|
for cmakelists in cmakelists_files:
|
|
cmakelists_dir = cmakelists.parent
|
|
libraries = parse_cmakelists(cmakelists)
|
|
|
|
for lib in libraries:
|
|
# Nur SHARED/MODULE brauchen Export-Header
|
|
if lib['type'] not in ('SHARED', 'MODULE'):
|
|
# STATIC Libraries: Files sollten KEINEN Export-Header haben
|
|
for src_file in lib['files']:
|
|
file_path = cmakelists_dir / src_file
|
|
if file_path.exists():
|
|
file_to_target[str(file_path)] = (lib['name'], 'STATIC')
|
|
continue
|
|
|
|
# SHARED/MODULE: Files brauchen Export-Header
|
|
for src_file in lib['files']:
|
|
file_path = cmakelists_dir / src_file
|
|
if file_path.exists():
|
|
file_to_target[str(file_path)] = (lib['name'], lib['type'])
|
|
|
|
print(f"Mapped {len(file_to_target)} files to targets")
|
|
|
|
# Finde alle Header-Dateien
|
|
header_files = list(base_dir.rglob('*.h')) + list(base_dir.rglob('*.hpp'))
|
|
print(f"Checking {len(header_files)} header files...")
|
|
|
|
fixed_count = 0
|
|
skipped_count = 0
|
|
error_count = 0
|
|
removed_count = 0
|
|
|
|
fixed_files = []
|
|
removed_files = []
|
|
error_files = []
|
|
|
|
for header_path in header_files:
|
|
header_str = str(header_path)
|
|
|
|
# Überspringe Files in include/grantlee/ (externer Code)
|
|
if 'include/grantlee' in header_str:
|
|
skipped_count += 1
|
|
continue
|
|
|
|
# Überspringe export.h Files selbst
|
|
if header_path.name.endswith('_export.h'):
|
|
skipped_count += 1
|
|
continue
|
|
|
|
if header_str not in file_to_target:
|
|
# File ist keinem Target zugeordnet - könnte in include/ sein
|
|
# Durchsuche alle CMakeLists.txt Dateien in Parent-Verzeichnissen
|
|
cmakelists_list = find_cmakelists_for_file(header_path, base_dir)
|
|
|
|
# Wenn kein CMakeLists.txt im Parent-Pfad, durchsuche alle
|
|
if not cmakelists_list:
|
|
# Suche in allen CMakeLists.txt nach diesem File
|
|
for cmakelists in cmakelists_files:
|
|
target_name, lib_type = search_file_in_cmakelists(header_path, cmakelists)
|
|
if target_name:
|
|
file_to_target[header_str] = (target_name, lib_type)
|
|
break
|
|
else:
|
|
# CMakeLists.txt gefunden, prüfe ob File dort referenziert ist
|
|
target_name, lib_type = search_file_in_cmakelists(header_path, cmakelists_list)
|
|
if target_name:
|
|
file_to_target[header_str] = (target_name, lib_type)
|
|
|
|
if header_str not in file_to_target:
|
|
error_count += 1
|
|
error_files.append((header_path, "Could not determine target"))
|
|
continue
|
|
|
|
target_name, lib_type = file_to_target[header_str]
|
|
|
|
if lib_type == 'STATIC':
|
|
# STATIC Library - Export-Header entfernen
|
|
fixed, old_include = remove_export_header_from_static(header_path)
|
|
if fixed:
|
|
removed_count += 1
|
|
removed_files.append((header_path, old_include, f"Target '{target_name}' is STATIC"))
|
|
else:
|
|
# SHARED/MODULE Library - Export-Header korrigieren
|
|
fixed, old_include, new_include = fix_export_header_in_file(header_path, target_name, lib_type)
|
|
if fixed:
|
|
fixed_count += 1
|
|
fixed_files.append((header_path, old_include, new_include, f"Target '{target_name}' ({lib_type})"))
|
|
else:
|
|
skipped_count += 1
|
|
|
|
# Ausgabe
|
|
print("\n" + "="*80)
|
|
print("Summary:")
|
|
print(f" Fixed: {fixed_count}")
|
|
print(f" Removed (STATIC): {removed_count}")
|
|
print(f" Skipped: {skipped_count}")
|
|
print(f" Errors: {error_count}")
|
|
print("="*80)
|
|
|
|
if fixed_files:
|
|
print("\nFixed files:")
|
|
for header_path, old_inc, new_inc, info in fixed_files[:20]:
|
|
rel_path = header_path.relative_to(base_dir)
|
|
print(f" FIX: {rel_path}")
|
|
print(f" {old_inc} -> {new_inc}")
|
|
print(f" ({info})")
|
|
if len(fixed_files) > 20:
|
|
print(f" ... and {len(fixed_files) - 20} more")
|
|
|
|
if removed_files:
|
|
print("\nRemoved export headers from STATIC libraries:")
|
|
for header_path, old_inc, info in removed_files[:20]:
|
|
rel_path = header_path.relative_to(base_dir)
|
|
print(f" REMOVE: {rel_path}")
|
|
print(f" {old_inc}")
|
|
print(f" ({info})")
|
|
if len(removed_files) > 20:
|
|
print(f" ... and {len(removed_files) - 20} more")
|
|
|
|
if error_files:
|
|
print("\nErrors:")
|
|
for header_path, error in error_files[:20]:
|
|
rel_path = header_path.relative_to(base_dir)
|
|
print(f" ERROR: {rel_path} - {error}")
|
|
if len(error_files) > 20:
|
|
print(f" ... and {len(error_files) - 20} more")
|
|
|
|
if __name__ == '__main__':
|
|
main() |