Hilfreiche Scripts für die C++/Qt Entwicklung, Code Reviews und Quality Assurance
Ein Skript zum Sortieren der SOURCES und HEADER Dateinamen in QMake pro Dateien
Um das Mergen von Qt-Code mit pro-Dateien zu vereinfachen, kann man die Einträge in den SOURCES und HEADER-Abschnitten von pro-Dateien vorher sortieren, dann sieht man besser, welche Dateien hinzugekommen sind oder entfernt wurden.
Das Skript sort_filenames_in_pro_files.py erwartet Tab-Zeichen als Einrückung in den pro-Dateien.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/env python3
# sort_filenames_in_pro_files.py
# Copyright 2025 Andreas Nicolai <andreas.nicolai -[at]- gmx.net>
#
# Script to automatically sort entries in SOURCES and HEADER sections of all
# qmake pro files below the given top-level directory.
#
# Syntax: sort_filenames_in_pro_files.py [<directory>]
#
# <directory> is the top-level directory, below which pro files
# are searched for (recursively). If missing, the current working
# directory will be used.
import os
import re
from pathlib import Path
def find_pro_files(root_dir):
pro_files = []
for dirpath, dirnames, filenames in os.walk(root_dir):
for filename in filenames:
if filename.endswith('.pro'):
full_path = os.path.join(dirpath, filename)
pro_files.append(full_path)
return pro_files
def process_multiline_section(lines, start_idx):
"""Extract, sort, and reformat a multiline += section."""
section_lines = []
idx = start_idx + 1
while idx < len(lines):
line = lines[idx].rstrip()
if not line or line.startswith("#"):
break
if re.match(r'^\w+\s*\+=', line): # another section starts
break
section_lines.append(line.strip(' \\'))
idx += 1
# Sort and reformat with backslashes
section_lines = sorted(section_lines)
for i in range(len(section_lines) - 1):
section_lines[i] += ' \\\n'
if len(section_lines) > 0:
section_lines[-1] += '\n'
return section_lines, idx
def process_file(filepath):
with open(filepath, 'r', encoding='utf-8') as f:
lines = f.readlines()
i = 0
while i < len(lines):
line = lines[i].strip()
if line.startswith('SOURCES +='):
sorted_section, end_idx = process_multiline_section(lines, i)
lines = lines[:i+1] + sorted_section + lines[end_idx:]
i = end_idx
elif line.startswith('HEADERS +='):
sorted_section, end_idx = process_multiline_section(lines, i)
lines = lines[:i+1] + sorted_section + lines[end_idx:]
i = end_idx
else:
i += 1
with open(filepath, 'w', encoding='utf-8') as f:
f.writelines(lines)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description="Sort filenames in all .pro files with a directory tree.")
parser.add_argument("directory", nargs="?", default=os.getcwd(),
help="Root directory to scan (default: current working directory)")
args = parser.parse_args()
pro_files = find_pro_files(args.directory)
print(f"\nProcessing .pro files below '{args.directory}':")
for file in pro_files:
print(file)
process_file(file)
Ausgeführt wird es wie folgt:
1
2
3
4
5
# Suche unterhalb von '/path/to/source/code'
> python sort_filenames_in_pro_files.py /path/to/source/code
# oder suche unterhalb des aktuellen Arbeitsverzeichnisses
> python sort_filenames_in_pro_files.py
Coding Style: Konsistente Include Guards in Header-Dateien
Entsprechend der Coding-Guidelines ist es sinnvoll, in C++ Header-Dateien Include-Guards nach einheitlichem Schema zu verwenden.
Motivation
Bspw. für eine Klasse MeineKleineKlasse
in der Header-Datei MeineKleineKlasse.h
:
1
2
3
4
5
6
7
8
9
#ifndef MeineKleineKlasseH
#define MeineKleineKlasseH
class MeineKleineKlasse {
// ...
};
#endif // MeineKleineKlasseH
oder entsprechend dem Qt-Standard:
1
2
3
4
5
6
7
8
9
#ifndef MEINEKLEINEKLASSE_H
#define MEINEKLEINEKLASSE_H
class MeineKleineKlasse {
// ...
};
#endif // MEINEKLEINEKLASSE_H
oder bei Verwendung eines Namespaces MEINS in der Header-Datei MEINS_MeineKleineKlasse.h
:
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef MEINS_MeineKleineKlasseH
#define MEINS_MeineKleineKlasseH
namespace MEINS {
class MeineKleineKlasse {
// ...
};
} // namespace MEINS
#endif // MEINS_MeineKleineKlasseH
Automatisieren mit Python-Script
Wenn man jetzt häufiger mal neue Klassen erstellt (entweder mittels Qt Creator oder als Kopie von existierenden Dateien), sollten die Kommentare sinnvoll angepasst werden. Wenn man das häufiger mal vergisst, kann man mein kleines Python-Script nutzen fix_header_guards.py, um hier automatisch folgendes zu machen:
Bei Header-Dateien:
- ‘namespace’ finden (falls deklariert) und Namen merken
- ‘class’ finden und Namen merken, falls nicht gefunden
- falls Klasse gefunden, Header Guard aus Namespace + Klassenname zusammenbauen und im Quelltext ersetzen
- Warnen, falls Dateiname nicht dem Namensschema entspricht
- Lizenz-Header einfügen (optional)
Bei Cpp-Dateien:
- Lizenz-Header einfügen (optional)
Die Funktionen headerGuard(className, namespaceName)
und baseName(className, namespaceName)
können entsprechend
eigener Vorlieben/Coding-Styles angepasst werden.
Ausführen des Scripts
Syntax: python fix_header_guards.py [--license=/path/to/LICENSE] /path/to/headerfiles
1
2
# Header-Guards für alle Dateien im aktuellen Verzeichnis aktualisieren
> python fix_header_guards.py .