Conan Paketmanager für C/C++
Beschäftigt man sich intensiver mit der Entwicklung von C-/C++-Programmen, so gibt es immer mal wieder den Fall, dass man externe Bibliotheken verwenden möchte. Findet die Entwicklung nur auf einem System statt, ist es in diesem Fall meist noch einfach, sich seine Abhängigkeiten mit dem Packetmanager des Systems zu installieren. Aber spätestens wenn eine CI im Einsatz ist, für mehrere verschiedene Architekturen und Zielsysteme gebaut wird oder die abhängigen Bibliotheken gepatched werden müssen, wird es knifflig.
Im Prinzip müsste dann jeder Entwickler (und die CI) alle Abhängigkeiten immer selbst bauen, was fehleranfällig sein kann. Auf einen System baut etwas nicht, weil andere Compiler verwendet werden, usw.
An dieser Stelle empfiehlt es sich, etwas Zeit in die integration einer brauchbaren Lösung zu investieren. Bis vor einiger Zeit haben wir hier auf eigene Skripte zurückgegriffen, mittlerweile gibt es aber in der Open-Source-Welt eine Handvoll Projekte, die diese Skripte überflüssig machen können. Eines davon ist conan, welches wir hier im Folgenden kurz vorstellen wollen.
Was ist conan?
Conan ist ein Paket-Manager für C/C++. Pakete schnüren sich aus fein konfigurierbaren Rezepten (oder besser “Bauanleitungen” für klassische C-/C++-Bibliotheken), Header- und pkg-config Informationen, sowie auch vorkompilierten Bibliotheken oder Programmen, die dann für den Entwickler direkt nutzbar sind. Pakete können in öffentlichen (z.B. Conan Center, Bincrafters) oder selbst gehosteten Repositorien (z. B. Artifactory und gitlab) vorgehalten werden - den so genannten remotes.
Wie ein Paket kompiliert werden soll, entscheiden Profile. Neben systemspezifischen Informationen - wie z. B. Compiler-Versionen und Architektur - entscheiden diese auch, ob eine bestimmte cmake- oder configure-Option beim Erstellen der Library gesetzt sein soll. Conan Center und Bincrafters bieten eine mitterlweile überwältigende Anzahl von vorkompilierten Binaries und Paketrezepten, in denen viel Wissen über die Kompilierung der jeweiligen Software steckt.
Letztendlich sind die vorkompilierten Binaries “ganz nett”, allerdings passen sie in der Realität meist nicht auf die doch mitunter speziellen Konfigurationsbedürfnisse der Projekte, sodass eigene Kopien von Rezepten und Binary-Repositories die Regel sein dürften.
Hat man alle gewünschten Pakete beisammen, werden diese in einer conanfile.txt
(oder dynamisch in einer
conanfile.py
) Datei abgelegt und mit dem eigenen Projekt verteilt. Diese Datei liefert die Eingabe
für Generatoren, die jeweils wissen wie z. B. eine cmake-Datei oder eine qmake-Datei mit den
in den Paketen gelieferten Informationen gebaut werden kann. Sie wissen also z. B. um die ganzen -I...
oder
-l...
Flags und wo sie angegeben werden müssen.
Ist das eigene Projekt dann passend konfiguriert, kann es wie gewohnt gebaut werden. Alle durch conan definierten Abhängigkeiten werden entsprechend berücksichtigt.
Installation
Wir beschäftigen uns im Folgenden mit Linux- bzw. Unix-ähnlichen Systemen. Informationen zur Installation, bzw. der Installation auf anderen Systemen können in der offiziellen Installationsdokumentation nachgelesen werden.
Soll immer die aktuellste Version von Conan (Python 3 erforderlich!) installiert sein, so ist pip
der bevorzugte Weg:
pip3 install --user conan
Möchte man aktualisieren, so ist nach dem install
noch ein --upgrade
einzufügen. Bei der obigen Variante
ist sicherzustellen, dass die mittels --user
installierten Daten auch im PATH
verfügbar sind. Auf unseren Systemen ist
das z. B. ~/.local/bin/conan
, auf MacOS sind die Binaries versionsabhängig meist unter Verzeichnissen wie
/Users/user/Library/Python/3.8/bin
zu finden.
Erste Einrichtung
Wie eingangs erwähnt, arbeitet conan mit Profilen. Ein Standardprofil kann man mit dem folgenden Befehl erstellen und damit gleich überprüfen, ob die Installation funktioniert:
$ conan profile new --detect default
Found gcc 10
Found clang 10.0
gcc>=5, using the major as version
************************* WARNING: GCC OLD ABI COMPATIBILITY ***********************
Conan detected a GCC version > 5 but has adjusted the 'compiler.libcxx' setting to
'libstdc++' for backwards compatibility.
Your compiler is likely using the new CXX11 ABI by default (libstdc++11).
If you want Conan to use the new ABI for the default profile, run:
$ conan profile update settings.compiler.libcxx=libstdc++11 default
Or edit '/home/cajus/.conan/profiles/default' and set compiler.libcxx=libstdc++11
************************************************************************************
Profile created with detected settings: /home/cajus/.conan/profiles/default
Grunsätzlich hat das schon mal geklappt, allerdings warnt uns das (Linux) System nun, dass eine rückwärtskompatible ABI des Compilers verwendet wird. Einstellungen kann man über das conan Tool erledigen. Wir sind ja ‘modern’, daher probieren wir direkt die vorgeschlagene Profiländerung aus:
conan profile update settings.compiler.libcxx=libstdc++11 default
Diese aktualisiert die Einstellung von settings.compiler.libcxx
im Profile default
auf libstdc++11
, was direkt überprüft werden kann:
$ cat /home/cajus/.conan/profiles/default
[settings]
os=Linux
os_build=Linux
arch=x86_64
arch_build=x86_64
compiler=gcc
compiler.version=10
compiler.libcxx=libstdc++
build_type=Release
[options]
[build_requires]
[env]
Das Profil ist also eine Datei im ini
-Stil und deutet bereits an, dass es noch weitere Sektionen mit Konfigurationsmöglichkeiten gibt. Dazu später mehr.
Remotes
Zur initialen Konfiguration fehlen nun unter Umständen noch Remotes, also die Repositories, die Rezepte/Binärdaten für Pakete enthalten. Schauen wir zunächst einmal, ob Remotes voreingestellt sind:
$ conan remote list
WARN: Remotes registry file missing, creating default one in /home/cajus/.conan/remotes.json
conan-center: https://conan.bintray.com [Verify SSL: True]
Remotes werden also offensichtlich in der remotes.json
hinterlegt und das conan-Skript hat
uns (ungefragt) bereits ein Remote für das Conan Center angelegt. Da wir im weiteren Verlauf
ein Paket benötigen, welches im Conan Center nicht verfügbar ist, fügen wir nun noch ein weiteres
Remote für Bincrafters hinzu:
$ conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan
Nun haben wir zwei Remotes: default
und bincrafters
Möchte man ein privates Repository hinzufügen, so kann man für einzelne Remotes auch Benutzer und
Passwort festlegen. Der folgende Befehl ist ein Beispiel und würde für das Remote bincrafters
den Benutzernamen klaus
und das Passwort secret
setzen. Funktioniert natürlich so nicht, daher
wie gesagt nur exemplarisch:
conan user -p secret -r bincrafters klaus
Bei einigen conan-Unterkommandos kann angegeben werden, welches Remote verwendet werden soll.
Pakete
Pakete sind - wie bereits erwähnt - eine Sammlung von Informationen rund um eine Bibliothek, ein Programm oder auch eine Toolchain (wie z. B. ein Android NDK oder Emscripten). Pakete haben einen Namen, eine Version, sind einem Namespace zugeordnet und sind Teil eines Channels. Die Notation ist wie folgt:
paketname/version@namespace/channel
oder als Beispiel:
qt/5.15.1@gonicus/stable
Letzteres beschreibt Qt in der Version 5.15.1 und wurde von uns für den Channel stable
selbst gebaut.
Sucht man ein bestimmtes Paket, kann man das Unterkommando search
verwenden:
$ conan search bzip2 --remote conan-center
Existing package recipes:
bzip2/1.0.6
bzip2/1.0.6@conan/stable
bzip2/1.0.6@conan/testing
bzip2/1.0.8
bzip2/1.0.8@conan/stable
Informationen zu einem der gefunden Paketen lassen sich über das Unterkommando info
abfragen:
$ conan info bzip2/1.0.8@conan/stable
bzip2/1.0.8@conan/stable: Not found in local cache, looking in remotes...
bzip2/1.0.8@conan/stable: Trying with 'conan-center'...
Downloading conanmanifest.txt completed [0.16k]
Downloading conanfile.py completed [2.14k]
Downloading conan_export.tgz completed [0.75k]
Decompressing conan_export.tgz completed [0.00k]
bzip2/1.0.8@conan/stable: Downloaded recipe revision 0
bzip2/1.0.8@conan/stable
ID: 91a8b22c2c5a149bc617cfc06cdd21bf23b12567
BuildID: None
Remote: conan-center=https://conan.bintray.com
URL: https://github.com/conan-community/conan-bzip2
Homepage: http://www.bzip.org
License: bzip2-1.0.8
Author: Conan Community
Description: bzip2 is a free and open-source file compression program that uses the Burrows–Wheeler algorithm.
Topics: conan, bzip2, data-compressor, file-compression
Recipe: Downloaded
Binary: Missing
Binary remote: conan-center
Creation date: 2019-08-20 11:27:12
Weitere Informationen liefert unter anderem inspect
. Interessant für den nachfolgenden Punkt
sind die options, die den Build des Paketes beeinflussen:
$ conan inspect -a options -r bincrafters bzip2/1.0.8@conan/stable
options:
build_executable: [True, False]
fPIC: [True, False]
shared: [True, False]
Möchte man vorkompilierte Binaries verwenden, lohnt sich noch ein Blick auf die Standard-Einstellungen, die
mittels -a default_options
abgefragt werden können:
$ conan inspect -a default_options -r conan-center bzip2/1.0.8@conan/stable
default_options:
build_executable: True
fPIC: True
shared: False
Die gefundenen Pakete können dann im eigenen Projekt referenziert werden. Das Erstellen bzw. Anpassen von Paketen liegt nicht im Fokus dieses Artikels.
Profile
Neben den eigentlichen Paketen ist deren gewünschte Konfiguration wichtig, da Projekte bestimmte Anforderungen haben. So sollen Bibliotheken vielleicht nur mit den benötigten Features übersetzt oder es soll ein statischer Build erzeugt werden. All dies kann durch eine Erweiterung des Basisprofils erreicht werden:
include(default)
[options]
bzip2:build_executable=False
bzip2:fpic=True
bzip2:shared=False
Dieses Beispiel inkludiert das default
-Profil und stellt unsere Anforderungen an den bzip2-Build: wir wollen
keine bzip2-Programmdateien, einen statischen Build als libbz2.a
und eine Konfiguration mit fpic
.
Baut man sein Projekt mit dieser Konfiguration, würde bzip2
mit der passenden Konfiguration erstellt, um dann
zur Verfügung zu stehen. Wie so ein “Bauvorgang” aussieht, schauen wir uns im Folgenden anhand des aus früheren Posts
bekannten Qt-/Qml-Projektes an.
Nutzung im Projekt
Als Basis soll für diesen Artikel das vorbereitete Mini-Programm dienen. Es kann wie folgt bezogen und danach in das Projektverzeichnis gewechselt werden:
git clone https://github.com/gonicus/font-demo.git
cd font-demo
Der nun folgende Vorgang benötigt viel Zeit, da im Verlauf Qt kompiliert wird.
Remotes
Da es sich um ein Qt-Projekt handelt, muss letzteres natürlich installiert sein. Dies kann man über den Paketmanager des Betriebsystems erreichen oder über die von The Qt Company bereitgestellten Builds. In diesem Fall benutzen wir aber von conan bereitgestellte Qt-Pakete aus dem bincrafters-Projekt. Falls noch nicht geschehen, muss das bincrafters-Repository hinzugefügt werden:
conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan
conanfile.txt
Im ersten Schritt müssen wir auf oberster Ebene unseres Projektes eine Datei mit dem Namen conanfile.txt
anlegen. Diese enthält
die Abhängigkeiten unseres Projektes - also zu allererst Qt. Den genauen Namen erfahren wir durch einen search
-Aufruf:
$ conan search qt -r bincrafters
Existing package recipes:
qt/5.9.8@bincrafters/stable
qt/5.11.3@bincrafters/stable
qt/5.12.0@bincrafters/stable
qt/5.12.1@bincrafters/stable
qt/5.12.2@bincrafters/stable
qt/5.12.3@bincrafters/stable
qt/5.12.4@bincrafters/stable
qt/5.12.5@bincrafters/stable
qt/5.12.6@bincrafters/stable
qt/5.12.7@bincrafters/stable
qt/5.12.8@bincrafters/stable
qt/5.12.9@bincrafters/stable
qt/5.13.0@bincrafters/stable
qt/5.13.1@bincrafters/stable
qt/5.13.2@bincrafters/stable
qt/5.14.0@bincrafters/stable
qt/5.14.1@bincrafters/stable
qt/5.14.2@bincrafters/stable
qt/5.15.0
qt/5.15.0@bincrafters/stable
qt/5.15.1@bincrafters/stable
Von Interesse ist natürlich die neueste Version - hier also qt/5.15.1@bincrafters/stable
- welche direkt in der Datei
conanfile.txt
eingetragen wird:
[requires]
qt/5.15.1@bincrafters/stable
Qt besitzt ein eigenes Werkzeug zum Erstellen von Makefiles, welches wir hier benutzen wollen: qmake
. Damit
conan die notwendigen Compile- und Linker-Flags korrekt aufbereiten kann, müssen wir noch den gewünschten
Generator konfigurieren:
[requires]
qt/5.15.1@bincrafters/stable
[generators]
qmake
Mit dieser conanfile.txt
Datei können wir nun conan damit beauftragen, uns die für die Abhängigkeiten
notwendigen Rezepte zu beschaffen. Allerdings kann es sein, dass wir noch bestimmte Optionen für den Bau
von Qt benötigen: wir möchten eine Qml-Anwendung erstellen, die auf jeden Fall qtdeclarative benötigt.
Schauen wir uns einmal an, welche Optionen das Paket qt/5.15.1@bincrafters/stable
bietet:
$ conan inspect -a default_options -r bincrafters qt/5.15.1@bincrafters/stable
default_options:
GUI: True
commercial: False
config: None
cross_compile: None
device: None
multiconfiguration: False
opengl: desktop
openssl: True
qt3d: False
qtactiveqt: False
qtandroidextras: False
qtcharts: False
qtconnectivity: False
qtdatavis3d: False
qtdeclarative: False
qtdoc: False
qtgamepad: False
qtgraphicaleffects: False
qtimageformats: False
qtlocation: False
qtlottie: False
qtmacextras: False
qtmultimedia: False
qtnetworkauth: False
qtpurchasing: False
qtqa: False
qtquick3d: False
qtquickcontrols: False
qtquickcontrols2: False
qtquicktimeline: False
qtremoteobjects: False
qtrepotools: False
qtscript: False
qtscxml: False
qtsensors: False
qtserialbus: False
qtserialport: False
qtspeech: False
qtsvg: False
qttools: False
qttranslations: False
qtvirtualkeyboard: False
qtwayland: False
qtwebchannel: False
qtwebengine: False
qtwebglplugin: False
qtwebsockets: False
qtwebview: False
qtwinextras: False
qtx11extras: False
qtxmlpatterns: False
shared: True
sysroot: None
widgets: True
with_doubleconversion: True
with_fontconfig: True
with_freetype: True
with_glib: True
with_harfbuzz: True
with_icu: True
with_libalsa: False
with_libjpeg: True
with_libpng: True
with_mysql: True
with_odbc: True
with_openal: True
with_pcre2: True
with_pq: True
with_sdl2: True
with_sqlite3: True
with_vulkan: False
with_zstd: True
Das ist wahrlich eine Flut von Optionen… Schaut man genauer hin, fällt aber ein qtdeclarative: False
ins Auge.
Das bedeutet, dass die vorkompilierten Binaries nicht für uns geeignet sind, weil sie qtdeclarative
deaktiviert
haben. Wir müssen also noch etwas tun, bevor unser erster Projekt-Build von Erfolg gekrönt ist.
Profil anpassen
Profile sind uns ja bereits eingangs begegnet. Neben Kommandozeilen-Optionen des conan-Skriptes stellen
sie an dieser Stelle eine Möglichkeit dar, die Parametrierung für den Qt-Build anzupassen - und zwar über die
oben bereits erwähnte [options]
-Sektion.
Erstellen wir die Datei custom.profile
:
include(default)
[options]
qt:qtdeclarative=True
Zunächst haben wir unser default
-Profil inkludiert und dann die Option qt:qtdeclarative
überschrieben. Vor dem
Doppelpunkt findet sich der Paketname, dahinter der Optionsname.
Neben der Einstellung von Paketoptionen, kann man in Profilen auch Toolchains installieren. Diese können notwendig sein, um die Software auf bestimmten Platformen zu bauen (z.B. benötigt man für Android andere Compiler-Einstellungen, als für das Host-System).
Verwenden wir nun das custom.profile
zum Erzeugen unseres Projektes unter Linux, wird ein passendes Qt für uns erzeugt.
Projekt erstellen
Möchten wir das Projekt erstellen, müssen wir vor dem gewohnten Erstellungsvorgang conan anweisen unsere Abhängigkeiten bereitzustellen. Dies geschieht mit dem Kommando:
conan install --build=missing --profile custom.profile .
conan lädt nun unser eben erstelltes Profil custom.profile
und schaut im aktuellen Verzeichnis (.
)
nach der Datei conanfile.txt
(bzw. conanfile.py
für dynamische Konfigurationen). Mit diesen Informationen
fängt es nun an, für das aktuelle Profil alle Abhängigkeiten von Qt sowie Qt selbst zu erstellen.
Lässt man das --build=missing
weg, wird der Build fehlschlagen, da es keine vorgebauten Pakete
mit unserer Konfiguration gibt.
Wer schon einmal Qt kompiliert hat weiss, dass nun ein guter Zeitpunkt für eine ausgiebige Kaffepause ist…
Es kann sein, dass conan System-Abhängigkeiten nachinstallieren möchte (z. B. bestimmte X11-Header). Tut man das nicht, wird der Build zwangsläufig fehlschlagen.
Nachdem der Build abgeschlossen ist, können wir einen Blick in das aktuelle Verzeichnis werfen und sehen einige
von conan erzeugte Dateien. Interessant für uns ist conanbuildinfo.pri
, welche vom
qmake
-Generator angelegt wurde. Hier steht alles was zum Kompilieren notwendig ist. Sagen wir es
qmake, indem wir am Anfang unserer Projektdatei ein include(conanbuildinfo.pri)
einfügen:
include(conanbuildinfo.pri)
QT += quick gui
CONFIG += c++11 qmltypes
SOURCES += \
IconFont.cpp \
main.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
QML_IMPORT_NAME = Demo
QML_IMPORT_MAJOR_VERSION = 1
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
IconFont.h
Nun gibt es noch eine letzte Qt- und conan-spezifische Hürde zu nehmen: Um ein Qt-Projekt bauen zu können, muss man qmake aufrufen - und zwar das richtige. Dieses befindet sich jedoch in dem conan-Paket, dessen Pfad wir momentan noch nicht kennen.
Über Umwege kommt man an diesen heran:
$ conan info qt/5.15.1@bincrafters/stable --paths --profile custom.profile --only package_folder --package-filter qt/5.15.1@*
package_folder: /home/cajus/.conan/data/qt/5.15.1/bincrafters/stable/package/dda3c30f2f7ad8d8a148137d9e6d9aaf9b69bbe9
Es folgt der übliche Build-Prozess mit einem build
-Verzeichnis:
mkdir build
cd build
/home/cajus/.conan/data/qt/5.15.1/bincrafters/stable/package/dda3c30f2f7ad8d8a148137d9e6d9aaf9b69bbe9/bin/qmake ..
make
Jetzt haben wir das font-demo
gebaut und können es mittels ./demo
aufrufen.
QtCreator
Möchte man den Qt Creator mit dieser Qt-Version benutzen, so kann man diese mit Hilfe des oben herausgefundenen
package_folder
in den Einstellungen hinterlegen und dann in einem Kit
zuweisen. Solange man keine Patches für
Qt hat, ist die Handhabung momentan aber noch etwas “sperrig”. Allerdings regt sich momentan in der Qt-Community
etwas Interesse an conan, sodass dies bald vielleicht einfacher geht.
Fazit
Die Nutzung von conan für die Paketverwaltung fühlt sich zunächst etwas gewöhnungsbedürftig an. Letztendlich kann der oben beschriebene Prozess allerdings eine wichtige Komponente für das erfolgreiche Management größerer Projekte mit vielen Abhängigkeiten und dem Bauen für verschiedene Architekturen sein. Letztere sind (im Falles von Cross-Compiling) nur ein anderes Profil, für das gebaut wird.
Für exotischere Build-Konfigurationen können Anpassungen an den Rezepten notwendig sein. Aber ob man die Zeit nun für einen eigenen Satz Skripte oder für conan aufwendet (und die Änderungen an die Community zurückgibt), spielt eine untergeordnete Rolle. Und je besser die Rezepte in allen Lebenslagen funktionieren, umso mehr profitiert man von der gemeinsamen Arbeit an conan-Rezepten.