Paketmanager zum Verwalten von Bibliotheken und deren Abhängigkeiten gehören zum Werkzeugkasten moderner Sprachen wie C/C++. Hier zeichnet sich der Paketmanager Vcpkg von Microsoft durch seine Plattformunabhängigkeit und einfache Bedienung aus.
Kaum ein Entwickler schreibt den kompletten Quelltext einer Anwendung. Fertige Bibliotheken für Logging, Datenbankzugriff oder ein Geometrie-Kernel beschleunigen die Lösungsentwicklung. Das Verwenden von Bibliotheken verlangt, dass Compiler und Linker auf die Header-Dateien und DLLs der direkt oder indirekt verwendeten Bibliotheken zugreifen können. Werkzeuge wie Maven für Java oder Nuget für C# lösen die Aufgabe sehr elegant: Sie laden beim Bauen die angegebenen Bibliotheken aus dem Internet und lösen ihre transitiven Abhängigkeiten auf.
Solche Paketmanager verwenden dazu zwei verschiedene Dateitypen: erstens die Paketbeschreibung, zweitens die Bibliotheksdatei. Die Paketbeschreibung enthält neben dem Namen und Version der bereitgestellten Bibliothek eine Aufzählung der weiteren benötigten Bibliotheken. Die Bibliotheken selbst liegen abhängig von der genutzten Sprache zum Beispiel als Java-Archiv (JAR), als C#-DLL oder als Javascript-Datei vor. Beide Dateitypen werden in der Regel auf zentralen Servern wie Maven Central [1] oder Nuget.org [2] bereitgestellt und lassen sich von dort herunterladen.
Zur eigenen Anwendung wiederum gehört eine Paketbeschreibung, die die benötigten Bibliotheken aufzählt. Der Paketmanager lädt dann vor dem Kompilieren die darin angeforderten Paketbeschreibungen und Bibliotheksdateien vom zentralen Server. Er kümmert sich um das Auflösen transitiver Abhängigkeiten, bevor der Übersetzungsvorgang unter Verwendung der nun lokal vorliegenden Bibliotheksdatei startet.
Eigene Wege
Im C++-Umfeld erfolgte die Verwaltung von Bibliotheken und deren Abhängigkeiten meist manuell, bis sich in den letzten Jahren (Conan 2015, Vcpkg 2016) auch hier Lösungen etablieren konnten.
Eines dieser Werkzeuge ist Vcpkg [3], eine bei Microsoft entwickelte Open-Source-Lösung, die unter der MIT License auf Github zur Verfügung steht [4]. Der alternative Paketmanager Conan [5] wird von Jfrog entwickelt und steht ebenfalls unter der MIT License auf Github bereit [6]. Beide funktionieren als eigenständige Werkzeuge, lassen sich aber problemlos auch in gängige Build-Systeme wie Cmake oder Msbuild integrieren.
Während Conan auf vorkompilierte Bibliotheken setzt, baut Vcpkg die Libraries auf dem Entwicklungsrechner aus den Quellen. Grund dafür ist die Vielzahl an Plattformen und Compilern, die es schwierig macht, fertig kompilierte Bibliotheken für alle Kombinationen zentral vorzuhalten. Außerdem spart man sich das Problem, einen jederzeit verfügbaren Server zum Download der Paketbeschreibung und der Bibliotheken bereitstellen und gegen das Abladen von Spyware sichern zu müssen.
Abbildung 1 stellt den Ablauf hinter den Kulissen bei Vcpkg dar. Nach dem Start liest der Paketmanager das Manifest des Projekts, ermittelt die vom Projekt benötigten Abhängigkeiten und sucht sie im Cache. Steht eine Abhängigkeit noch nicht bereit, liest er deren Manifest aus der Registry und lädt die Projektquellen herunter. Anschließend baut der Paketmanager die Bibliothek und speichert sie im Cache. Nachdem das für alle direkten und indirekten Abhängigkeiten erfolgt ist, kann das Projekt selbst gebaut werden.
Bezüglich des Caches gibt es zwei Optionen: Der Classic-Modus verwendet einen zentralen Ablageort für alle Projekte. Im Manifest-Mode gibt es einen gesonderten Ablageort für jedes Projekt. Um Konflikte zwischen den Abhängigkeiten verschiedener Projekte zu vermeiden, empfiehlt sich in der Regel die Verwendung des Manifest-Modus.
Das Manifest einer Bibliothek besteht mindestens aus einer Datei namens »vcpkg.json«. Listing 1 zeigt ein Beispiel für die Fmt-Library [7]. Diese Datei enthält neben dem Namen der Bibliothek vor allem eine Aufzählung der benötigten Abhängigkeiten. Sowohl die Gültigkeit als auch die der Abhängigkeiten der Bibliothek lassen sich bei Bedarf auf bestimmte Plattformen einschränken. Komplexere Fälle, wie die Notwendigkeit anderer Repositories zum Auflösen von Abhängigkeiten, erzwingen zusätzliche Informationen in der optionalen Datei »vcpkg-configuration.json«. In Listing 2 finden Sie ein Beispiel für ein zugehöriges Cmake-File.
Listing 1
Manifest-Datei für Fmt
{
"name": "fmt",
"version": "9.1.0",
"port-version": 1,
"description": "Formatting library for C++. ... ",
"homepage": "https://GitHub.com/fmtlib/fmt",
"license": "MIT",
"dependencies": [
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
]
}
Listing 2
Cmake-Datei (Beispiel)
cmake_minimum_required(VERSION 3.25)
set(VCPKG_EXAMPLE_VERSION 0.1.0)
project(vcpkg-example VERSION ${VCPKG_EXAMPLE_VERSION} LANGUAGES CXX)
# Dependencies
find_package(fmt CONFIG REQUIRED)
find_package(Boost REQUIRED COMPONENTS program_options)
# Set libs to link against
list(APPEND VCPKG_EXAMPLE_LIBS
fmt::fmt
Boost::boost
Boost::program_options)
# Build
add_executable(${PROJECT_NAME} src/main.cc)
target_link_libraries(${PROJECT_NAME} PRIVATE ${VCPKG_EXAMPLE_LIBS})
Schritt für Schritt
Die Verwendung des Paketmanagers für eigene Programme gestaltet sich vergleichsweise einfach. Auf der Github-Seite von Vcpkg [8] findet sich ein komplettes Beispiel. Die Schritte zum Kompilieren auf der Kommandozeile fasst Listing 3 zusammen. Die Kommandos aus den ersten beiden Zeilen laden Vcpkg und das Beispielprogramm von Github. Um den Paketmanager vor der ersten Verwendung zu initialisieren, wechseln Sie in dessen Verzeichnis und rufen das Skript »bootstrap-vcpkg.sh« auf (Zeile 3 und 4).
Listing 3
Beispielprogramm kompilieren
$ git clone https://github.com/skfcz/vcpkgBeispiel.git $ git clone https://github.com/Microsoft/vcpkg.git $ cd vcpkgBeispiel $ sh vcpkg/bootstrap-vcpkg.sh [...] $ cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake $ cmake --build build/ build/vcpkg-example --option World
Anschließend rufen Sie im Ordner des Beispielprogramms Cmake so auf, dass es die Vcpkg-Toolchain zum Laden und Kompilieren der benötigten Bibliotheken nutzt. Wie Zeile 6 demonstriert, müssen Sie dabei die entsprechende Cmake-Option setzen. Dasselbe gilt analog auch bei der Verwendung einer IDE. Abbildung 2 zeigt den entsprechenden Eintrag für Clion.
Danach heißt es beim ersten Mal warten, denn nun löst Vcpkg die vom Beispielprogramm angeforderten Abhängigkeiten in der Registry auf, lädt deren Quellen herunter und kompiliert sie. Bei umfangreichen Dependencies nimmt das – abhängig von der Rechenleistung des Systems – unter Umständen einige Zeit in Anspruch.
Als Lohn der Mühe finden sich im Cache-Verzeichnis die Header-Dateien sowie die kompilierten Binaries der angeforderten Bibliotheken. Die benötigten Bibliotheken führt die Paketbeschreibung des Programms auf. Im Beispiel aus Listing 4 handelt es sich um eine Bibliothek zur Behandlung von Kommandozeilenoptionen sowie eine zum eleganten Formatieren von Strings.
Listing 4
Paketbeschreibung (Beispiel)
{
"name": "vcpkg-example",
"version": "0.1.0",
"dependencies": [
{
"name": "boost-program-options"
},
{
"name": "fmt"
}
]
}
Paketsammlung
Auf der Webseite von Vcpkg findet sich unter Packages eine Suchfunktion, um die richtigen Namen für die Paketbeschreibungseinträge zu finden. Unter den gut 2000 Bibliotheken sollte sich für die meisten Aufgaben eine Lösung finden lassen. Alternativ suchen Sie die Bibliotheken über den Kommandozeilenaufruf »vcpkg search«.
Nach dem Kompilieren der benötigten Bibliotheken können Sie im nächsten Schritt Ihr Programm mit Cmake kompilieren und gegen die eben erstellten Bibliotheken linken (Listing 3, Zeile 7). Das erfordert in der Cmake-Datei (Listing 2) keinerlei Änderungen gegenüber einer Version für lokal installierte Libraries – vorausgesetzt, die Bibliotheken lassen sich mit Cmake finden.
Fazit und Ausblick
Insgesamt bekommen Sie mit dem Paketmanager Vcpkg eine recht schlanke Lösung, um zusammen mit Cmake Ihre Quelltexte auf verschiedenen Plattformen zu kompilieren. Eine aufwendige Installation der benötigten Bibliotheken in der passenden Version können Sie sich somit sparen, da Vcpkg sie jeweils lokal baut.
Fairerweise muss man aber auch den initialen Aufwand erwähnen, der damit einhergeht. Das Installieren eines RPMs oder DEBs einer sehr großen Bibliothek wie OpenCascade ist in ein bis zwei Minuten erledigt, das Kompilieren dauert eher eine gute halbe Stunde. Nach dem erstmaligen Übersetzen der Abhängigkeiten gibt es jedoch keinen Zeitunterschied, und als Belohnung winkt ein plattformunabhängiges Build-System.
Das funktioniert nicht nur lokal: Pratik Chougule hat ein Projekt-Setup [9] für das Kompilieren auf Jenkins [10] bereitgestellt, mit Github-Actions lässt sich Vcpkg auch zum Bauen auf Github nutzen. Selbst, wenn Sie die eine oder andere kommerziellen Bibliotheken nutzen müssen, können Sie trotzdem von Vcpkg profitieren. In den Microsoft-Developer-Blogs erzählt Augustin Popa [11], wie sich solche Libraries in das Setup mit Vcpkg integrieren lassen. Von daher lohnt es sich für alle C- und C++-Entwickler, einen Blick auf dieses Werkzeug zu werfen. (jlu)
Die Autoren
Paul Büchner ist begeisterter Entwickler mit Schwerpunkt auf Web- und CAD-Software. Carsten Zerbst erstellt Software für Ingenieure vom einfachen Konverter bis hin zur unternehmenskritischen Integrationslösung. Er sucht für sein Team noch Verstärkung für abwechslungsreiche Tätigkeiten mit Java, C# oder C++ in Hamburg und bietet Themen für Bachelor- und Master-Arbeiten an.
Infos
- Maven Central: https://search.maven.org
- Nuget: https://www.nuget.org
- Vcpkg: https://vcpkg.io
- Vcpkg auf Github: https://github.com/microsoft/vcpkg
- Conan: https://conan.io
- Conan auf Github: https://github.com/conan-io/conan
- Fmt: https://github.com/fmtlib/fmt
- Beispiel: https://github.com/skfcz/vcpkg
- Jenkins-Projekt: https://github.com/planetpratik/CMake-Jenkins-CPP
- Jenkins: https://www.jenkins.io
- Closed Source: https://devblogs.microsoft.com/cppblog/registries-bring-your-own-libraries-to-vcpkg/








