
CONAN Builds mit CTest
« | 22 Sep 2024 | »Tests gehören zum Build-Vorgang fix dazu. Besonders wenn daraus ein Conan Paket werden soll.
Es macht also Sinn einen CTest
Lauf nach jedem CMake-Build auszuführen.
Einer der größeren Fehler, die mir und meinen Kollegen beruflich passiert ist,
war die fälschliche Nutzung von Conan
Test-Packages für Unit-Tests.
Test-Packages sind (wie in der Doku beschreiben) nur dafür da, die Pakete und
ihren Inhalt auf Anwesenheit zu testen, und nicht um komplexe interne
Funktionstests auszuführen.
Hier wurde viel herumgescriptet um im Endeffekt eine suboptimale Testreihe
auszuführen.
Die Lösung wäre so nahe gelegen … aber wir konnten sie alle nicht sehen:
cmake
bzw ctest
CTest
Der größte Vorteil von CTest
ist, dass sich Tests als Targets in den CMake
Baum einfügen, eine von außen abrufbare Schnittstelle haben und daher von IDEs
direkt oder indirekt unterstützt werden.
Ein Test ist in C/C++ in der Regel ein eigener Prozess, der mitgebaut wird,
aber nicht per cmake --install
weiterverteilt wird. Jedoch kann er durch den
Aufruf von ctest
im Build-Verzeichnis ausgeführt werden und sein Ergebnis wird
per Exit-Code als Erfolg oder Misserfolg in einen Bericht eingefügt.
So entstehen JUnit XML Dateien, die wiederum das Test-Ergebnis an Kontrollsysteme melden können.
Tests werden aktiviert, wenn man in der Haupt-CMakeLists.txt
-Datei die Zeilen
einfügt.
Für jedes Testprogramm ist dann noch ein eigenes Target notwendig, das als Test
registriert wird:
Idealerweise nutzt man dann noch ein fertiges Test-Framework wie boost::test
oder Google gtest
, welch per Standard den Exit-Code auf 0
oder !=0
im
Erfolgs- und Fehlerfall setzen.
Das CONAN-Dependency-Problem
Tja, und wenn man jetzt sein CONAN-Paket erstellt, sieht der Build+Test Vorgang etwa so aus:
Das bedeutet, dass in einem conan create
oder conan build
Prozess auch
alles funktioniert, da CONAN hier die Umgebungsvariablen so setzt, dass
Abhängigkeiten im CONAN-Cache per PATH
oder LD_LIBRARY_PATH
gefunden
werden.
Doch wer nur conan install
einsetzt und dann seinen Code z.B. in
Visual Studio Code ausführen und debuggen will, dem fehlen nun diverse
.dll
/.so
Pfade.
Bibliotheken die direkt gelinkt werden haben noch eine “gute Chance” gefunden
zu werden, aber alles was zur Laufzeit geladen wird wie z.B. Plugins oder
optionale Komponenten finden mit LoadLibrary()
oder dl_open()
ihr Ziel
im Conan Cache nicht.
Meine Lösung für dieses Problem war die Erzeugung einer .env
Environment
Datei, die mit allen Pfaden von bekannten Abhängigkeiten befüllt war.
Eine solche Datei konnte dann auch in CMakeLists.txt
der Test-Ausführung
zugeführt werden um somit die Tests mit dem vollständigen Environment
auszuführen.
1def gen_env_file_with_conan_deps(self, envfile_path: str): 2 # init with system-defined values 3 all_bin_paths = os.getenv('PATH', '').split(os.pathsep) 4 all_lib_paths = os.getenv('LD_LIBRARY_PATH', '').split(os.pathsep) 5 6 # add conan dependencies 7 for dep in self.dependencies.values(): 8 for lib in dep.cpp_info.libdirs: 9 add_lib_paths.append(str(lib)) 10 for bin in dep.cpp_info.bindirs: 11 add_bin_paths.append(str(bin)) 12 13 with open(envfile_path, 'w') as f: 14 f.write('PATH={}'.format( os.pathsep.join(add_bin_paths))) 15 f.write('LD_LIBRARY_PATH={}'.format( os.pathsep.join(add_bin_paths)))
Wird diese Methode von Conan’s generate()
Methode aus mit
self.gen_env_file_with_conan_deps(os.path.join(self.build_folder, '.env'))
aufgerufen, dann kann diese Datei von Visual-Studio-Code
Launch-tasks benutzt oder von CMake
ausgelesen werden und mit ein paar
Hacks als ENVIRONMENT
Eigenschaft des Tests gesetzt werden:
1if(EXISTS "${CMAKE_BINARY_DIR}/.env") 2 file(READ "${CMAKE_BINARY_DIR}/.env" envfile_content) 3 string(REPLACE "\r" "" envfile_content "${envfile_content}") 4 string(REPLACE ";" "\\;" envfile_content "${envfile_content}") 5 string(REPLACE "\n" ";" envfile_entries "${envfile_content}") 6 set_property(TEST ${PROJECT_NAME} PROPERTY ENVIRONMENT "${envfile_entries}") 7endif()
Fazit
Tests sind super, Tests sind gut … aber können bei erhöhter Komplexität auch anstrengend werden.
Ein grobes Problem mit Conan-Builds ist, dass Tests innerhalb der
Conan
Umgebung immer anders laufen, als während der Entwicklung innerhalb
einer IDE.
Und CMake
kennt in erster Line nur Build-Details, jedoch keine erweiterten
Umgebungs- und Ausführungsparameter.
Tja, so ist das Leben.
Aber es wäre ja langweilig, wenn immer gleich alles auf Anhieb funktioniert.