
CMake Install
«« | 21 Jul 2024 | »»CMake ist “das” Werkzeug zum Bauen von C/C++ Software. Doch danach soll diese auch verteilt werden können.
Und genau hierfür müssen die erzeugten Binärdateien und auch Interface Sourcecodes an die richtige Stelle geschoben werden.
Auf Grund meiner “Windows-Vergangenheit”, sah ich CMake
in erster Linie
immer als Build-Generator, der nur das Kompilieren kontrollieren soll.
In der Linux Welt ist die “Installation” des
fertigen Kompilats an die richtige Stelle im /
Dateisystem aber ebenso
wichtig. Lässt man CMake
auch diesen Schritt übernehmen,
ergeben sich gleich mehrere Vorteile.
install(TARGETS)
Wenn man die öffentlichen Header in einem Projekt auch als solche in
CMake
bekannt gegeben hat, dann können diese parallel zu den Binaries
auch gleich mitinstalliert werden.
Ein CMake
-Projekt sieht dann beispielsweise so aus:
1project(MyProject) 2 3file(GLOB MY_HEADERS "*.h") 4file(GLOB MY_SOURCES "*.c" "*.cpp" ) 5 6add_library(${PROJECT_NAME} 7 ${MY_HEADERS} ${MY_SOURCES}) 8 9set_target_properties(${PROJECT_NAME} PROPERTIES 10 PUBLIC_HEADER "${MY_HEADERS}" 11) 12 13target_link_libraries(${PROJECT_NAME} 14 mydependency 15) 16 17install(TARGETS ${PROJECT_NAME} 18 ARCHIVE DESTINATION "lib" 19 LIBRARY DESTINATION "lib" 20 RUNTIME DESTINATION "bin" 21 PUBLIC_HEADER DESTINATION "include" 22)
Wie in make
lässt sich auch in cmake
ein Prefix-Directory angeben und
in diesem werden die Dateien in die Unterverzeichnisse lib
, bin
und
include
am Ende installiert.
Der Standard-Prefix-Pfad ist /usr
unter Linux, womit wir am Ende bei
den Standardverzeichnissen /usr/bin
, /usr/lib
und /usr/include
landen.
Conan Pakete
Tatsächlich haben mich erst Conan
Pakete auf die Notwendigkeit einer korrekten Installation aufmerksam gemacht.
Wenn nämlich ein cmake
Install-Schritt alle Dateien in die
Unterverzeichnisse bin
, lib
und include
eines Zielverzeichnisses
verschieben kann, dann ist die Conan package()
Methode nichts weiter
als ein:
Runtime-Pfade
Wenn man komplexere Bibliotheken erstellt, ordnet man diese gerne in
Hierarchien von Unterverzeichnissen ein. Die Dateien liegen dann nicht
einfach in lib/
sondern z.B.: in lib/component/version/something.so
.
Wenn man jetzt ein Projekt mit Bibliotheken und Programmen baut, linken
die Programme direkt gegen die Pfade im Build-System.
Das wäre dann z.B.: ein ~/my_project/build/output/mycorp/mylib.so
.
Doch genau diesen Pfad wird es am Ende nicht mehr geben, weil wir die
.so
in /usr/lib/mycorp/mylib.so
installieren wollen.
Das Programm findet mylib.so
somit nicht mehr und verweigert die
Ausführung.
Zur Lösung dieses Problems hat jedes Programm das RPATH
Feld (Runtime-Path),
welches die Verzeichnisse enthält, wo .so
Dateien gesucht werden sollen.
cmake --install path/to/build_dir
ändert bei einer Installation eines
Programms auch diesen Pfad auf das neue Installationsziel um.
Dadurch erspart man sich üble Anpassungen der Library-Loader-Konfiguration
oder das Setzen von LD_LIBRARY_PATH
auf die eigenen Unterverzeichnisse.
Fazit
install(TARGETS)
ist nun in allen meinen Projekten nachgepflegt, wo es
bisher fehlte … und einem Tool wie Conan
gefällt das sehr.
Bisher war das Thema Runtime-Pfade für mich eher nebensächlich, da ich in
erste Linie statisch kompiliere und auf .dll
/ .so
Varianten gerne
verzichte.
Ein “ordentliches” Projekt muss aber mit beiden Möglichkeiten umgehen können
und somit gehören Shared Libraries und deren korrekte Installation auch zum
erwarteten Standard.
Es gibt da noch die interessante Möglichkeit den RPATH
“relativ” zu setzen
… aber das ist eine andere Geschichte.