UPDATE: In einem neuen Beitrag wird über die Erstellung eines korrekten Subdirs-Project mit Tests diskutiert. Deswegen sind die meisten Sachen im aktuellen Beitrag nicht mehr gültig.
Liebe Leserinnen, liebe Leser,
in diesem Beitrag werde ich manche meiner Befunde über die Einrichgung eines Projekts in Qt Creator teilen. Aus erster Sicht scheint das wie eine alltägliche Aufgabe zu sein, aber es erwies sich ein bisschen problematischer zu sein, als ich es gedacht habe.
Eine der ersten Sachen, an denen man denkt, wenn man ein neues Projekt startet, ist die Anordnung der Quelldateien. Natürlich wird der Programmierer die Dateien basierend auf einer Logik in verschiedenen Ordner setzten, sodass sie miteinander nicht mischen. Dennoch ist die Erstellung neuer Ordner und die Platzierung der Quelldateien in diese Ordner in Qt Creator nicht so einfach. Ich bin darüber nicht ganz sicher, warum dieser Entwurf bei Creator gewählt wurde, aber um neue Quelldateien in einen Projektunterordner zu platzieren, soll der Ordner mithilfe eines Dateimanagers zuerst erstellt werden, dann soll der Name des Ordners vor dem Namen der Quellendatei in dem ‘New File’-Dialog von Qt Creator eingetippt werden. Ein Beispiel ist unten zu sehen:
Hinzufügung von neuen Dateien zu einem Projektunterordner in Qt Creator
Wie offensichtlich ist, ist diese Funktion ein bisschen verdeckt, weil es keine selbsterklärende Weise für die Auswahl eines Ordners gibt. Es gibt auch dafür keine Hinweise, dass wenn der Name des Ordners eingetippt wird, wird das korrekt verwaltet. Glücklicherweise werden die Quelldateien automatisch in die Projektdatei einzufügt, und sie werden in den entsprechenden Ordnern im Projekt-Explorer erscheinen.
Nachdem diese alltägliche Sache erklärt wurde, ist das jetzt möglich zu diskutieren, wie ein Projekt mit Unit-Test erstellt werden kann. Weil Qt Creator mindestens zwei Buildsysteme und zwei Test-frameworks vorkonfiguriert unterstüzt, bekommt der Programmierer manche Flexibilität für die Erledigung einer Aufgabe. Ich werde alle Kombinationen und Möglichkeiten nicht untersuchen, sondern werde ich nur diejenigen Ansätze demonstrieren, die ich ausprobiert habe.
Eine Funktion von qmake ist die Möglichkeit für die Erstellung eines Subdirs-Projekts. Einfach gesagt bedeutet das, dass es etliche Unterordner gibt, die ihre eigene Quelldateien und qmake Projektdateien enthalten, und infolgedessen werden diese Subdirs-Projekten von der Hauptprojektdatei, die in dem Stammverzeichnis ist, verwaltet. Das ermöglicht eine feingranulare Kontrolle über die Platzierung der Quelldateien in verschiedenen Unterordner. Tatsächlich ähnelt sich diese Funktion zu einer Funktion von CMake (aber ich bin darüber nich sicher, ob CMake die Funktion genannt hat). Aus erster Sicht scheint das das perkte Merkmal zu sein, um ein Haupt- und Testprojekt zu erstellen, und um die obengenannte Flexibilität für die Kompilierung des Quellcodes zu verwenden. Eine Beispieldateistruktur für ein derartiges Projekt ist unten zu sehen:
MyProject
|--MyProject.pro
|--ProjSources
| |--GUI
| | |--guisrc.cpp
| | |--guisrc.h
| | \--guisrc.ui
| |--ProjSources.pro
| |--main.cpp
| |--mysource1.cpp
| |--mysource1.h
| |--mysource2.cpp
| \--mysource2.h
\--ProjTests
|--ProjTests.pro
|--main.cpp
|--tst_mysource1.cpp
\--tst_mysource2.cppObwohl diese Konfiguration vielversprechend aussieht, gab es leider ein Problem, das ich nicht beheben konnte: qmake hat das ganze Projekt als ein Testprojekt betrachtet, ungeachtet der Tatsache, dass nur das Testprojekt war wie ein Test definiert (die .pro-Datei enthielt “QT += testlib” und “CONFIG += testcase”). Das bedeutete, dass, obwohl die Quelldateien kompiliert werden konnten, die erzeugte Programmdatei des Hauptprogramms nicht ausgeführt wurde, bis auf die Programmdatei des Tests. Falls ich die Tests aus dem ganzen Projekt entfernt habe, startete die Hauptprogrammdatei wieder zu funktionieren. Ich bin darüber nicht ganz sicher, was das Problem verursacht hat, aber möglicherweise habe ich etwas übersehen. Nichtsdestoweniger, ich habe das erwartet, dass diese Konfiguration funktionieren wird, weil Qt Creator die .pro-Dateien am meistens automatisch erzeugt, und deswegen könnte man das behaupten, dass solche Konfigurationen in der Qt-Zentrale für Bedienkomfort getestet wurde.
NOTIZ: ich habe eine Lösung für dieses Problem gefunden. Die Lösung ist durch den Link am Ende dieses Beitrags zu finden.
Weil die Online-Dokumentation darüber nicht zu eindeutig war, wie diese Konfiguration zum Funktionieren gebracht werden kann, habe ich mich entschieden, damit nicht zu viele Zeit zu verbringen, besonders wenn man bedenkt, dass qmake durch (qbs) bald ersetzt wird. Dennoch habe ich mir einen alternativen Ansatz ausgeklügelt, der einen Kompromiss auf Flexibilität nicht zu viel schließt, und einfach zu konfigurieren ist. Die Vorstellung ist, ein Hauptprojekt (MyProject) zuerst zu erstellen, dann das Testprojekt (ProjTests) in den MyProject-Ordner zu platzieren. Das Testprojekt wird von einer eigenen, gesonderten Projektdatei verwaltet. Auf diese Weise werden sich alle Quelldateien im Stammverzeichnis befinden, und mithilfe einer Optimierung der Testprojektdatei werden beide Projects die gleichen Quelldateien verwenden.
MyProject
|--GUI
| |--guisrc.cpp
| |--guisrc.h
| \--guisrc.ui
|--ProjTests
| |--CMakeLists.txt
| |--main.cpp
| |--tst_mysource1.cpp
| \--tst_mysource2.cpp
|--MyProject.pro
|--main.cpp
|--mysource1.cpp
|--mysource1.h
|--mysource2.cpp
\--mysource2.hDieser Ansatz funktioniert ganz gut, weil qmake (oder CMake) kein Ordner oder Datei als Teil des Projekts betrachtet, solange es in der .pro-Datei nicht ausdrücklich spezifiziert ist. Ein aufmerksamer Leser hat das vielleicht bemerkt, dass ich CMake als der Projektmanager für das Testprojekt gewählt habe, und es gibt einen guten Grund dafür: wenn Dateien in das Hauptprojekt hinzugefügt werden, wird die Konfigurationsdatei des Testprojekts nicht automatisch aktualisiert. Weil CMake (im gegensatz zu qmake) in der Konfigurationsdatei keine ausdrückliche Nennung der Header-Dateien und .ui-Dateien braucht, ist das nicht zu schwer, die Datei manuell aufrechterzuhalten.
Abgesehen von der obengenannten manuellen Einfügung der zu testende Quelldateien ist die Menge der Bearbeitung der CMakeLists.txt Datei überrachend minimal: Um die obengenannte Ordnerstruktur zum Funktionieren zu bringen, soll man nur den entsprechenden Pfad, der sowohl absolut als auch relativ sein kann, der zu prüfenden Dateien eingeben. Dann soll der Pfad vorher den Namen der Quelldateien zu “add_executable()” hinzugefügt werden. Der Pfad soll zu “include_directories()” auch hinzugefügt werden. Wenn der Nutzer vorsichtig ist, und Dateien werden nur in bestimmten Fällen hinzugefügt (wenn das Hauptprojekt im Projektexplorer von Qt Creator aktiv ist), braucht die qmake-Datei keine manuelle Bearbeitung.
Die folgende CMake und qmake-Dateien zeigen, wie man mit der obengenannten Ordnerstruktur arbeiten kann, indem sowohl Qt als auch OpenCV Bibliotheken von dem Projekt verwendet wird. Und apropos Bibliotheken: wichtig ist festzuhalten, dass die Einfügung der zusätzlichen Frameworks nur in eine der Projektdateien nicht genug ist, weil das Test- und Hauptprojekt getrennt verwaltet werden. Hier sind die Dateien:
Hauptprojektdatei:
qmake
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
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = MyProject
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++14
INCLUDEPATH += ./GUI
SOURCES += \
main.cpp \
GUI/guisrc.cpp \
mysource1.cpp \
mysource2.cpp
HEADERS += \
GUI/guisrc.h \
mysource1.h \
mysource2.h
FORMS += \
GUI/guisrc.ui
win32: {
include("c:/dev/opencv/opencv.pri")
}
unix: !macx {
CONFIG += link_pkgconfig
PKGCONFIG += opencv
}
unix: macx {
INCLUDEPATH += "/usr/local/include"
LIBS += -L"/usr/local/lib" -lopencv_world
}
Projektdatei des Testprojekts:
CMake
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
CMAKE_MINIMUM_REQUIRED(VERSION 3.1)
PROJECT(ProjTests
LANGUAGES CXX)
set(MYPROJECT_DIR "..")
add_definitions(-DGTEST_LANGUAGE_CXX11)
set(CMAKE_CXX_STANDARD 14)
find_package(Threads REQUIRED)
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
find_package(OpenCV REQUIRED)
if ($ENV{GOOGLETEST_DIR})
SET(GOOGLETEST_DIR $ENV{GOOGLETEST_DIR})
else ()
message(WARNING "Using googletest src dir specified at Qt Creator wizard")
SET(GOOGLETEST_DIR "/home/user/Tools/googletest")
endif ()
if (EXISTS ${GOOGLETEST_DIR})
SET(GTestSrc ${GOOGLETEST_DIR}/googletest)
SET(GMockSrc ${GOOGLETEST_DIR}/googlemock)
else ()
message( FATAL_ERROR "No googletest src dir found - set GOOGLETEST_DIR to enable!")
endif ()
include_directories(${GTestSrc}
${GTestSrc}/include
${GMockSrc}
${GMockSrc}/include
${OpenCV_INCLUDE_DIRS}
${MYPROJECT_DIR}
${MYPROJECT_DIR}/GUI)
add_executable(${PROJECT_NAME} main.cpp
tst_mysource1.cpp
tst_mysource2.cpp
${MYPROJECT_DIR}/mysource1.cpp
${MYPROJECT_DIR}/mysource2.cpp
${GTestSrc}/src/gtest-all.cc
${GMockSrc}/src/gmock-all.cc)
add_test(${PROJECT_NAME} COMMAND ${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads Qt5::Core Qt5::Widgets ${OpenCV_LIBS})
Letztendlich ist es erwähnenswert, dass Qt Creator die Dateien außer dem Quellordner kompiliert. Das bedeutet, dass die Tests dank der aktuellen Einstellungen direkt in den MyProject-Order plaziert werden. Das ist natürlich nicht erwünscht, deshalb wird eine kleine Anpassung der “Build directory”-Variable im “Project Mode”-Fenster solche Probleme lösen.
Genauso wie mit vielen Sachen, es gibt Vor- und Nachteile mit dieser Methode. Ein Vorteil ist, dass, obwohl das Programm- und Testordernstrukturen getrennt behandelt werden, wird die kontinuerliche und nahtlose Entwicklung nicht verhindert, weil beide Projekte die gleiche Quelldateien verwenden. Ein Nachteil ist, dass die Quelldateien von der Entwicklungsumgebung nur zur Projektdatei automatisch hinzufügt werden, aber die Testprojektdatei muss manuell gewartet werden. Als es schon erwähnt wurde, ist es nicht eine große Hürde, wenn man für das Testprojekt CMake benutzt (bei großen Projekten könnte es als ein Problem erscheinen, aber ein kluges Skript könnte dieses Problem am Anfang beheben). Ein anderer Nachteil ist (für irgendeine könnte das auch ein Vorteil sein), dass die Tests müssen getrennt ausgeführt werden.
Wie immer, vielen Dank fürs Lesen.