BitBucket mit CMake und Caches

Der Source-Hoster BitBucket bietet neben GIT zur reinen Quellcodeverwaltung auch Tools zum automatischen Bauen von Software an. Dieses Continuous Integration Features (oder kurz “CI”) bietet Support für unterschiedliche Werkzeuge für alles mögliche Zeugs von dotNET Core bis Java.

Das wichtigste fehlt natürlich: nämlich CMake. Zum Glück kann man sich das aber selbst hinbiegen.


Kein CMake auf BitBucket?

OK, bevor man mir vorwirft die Unwahrheit zu sagen, sollte man präzisieren: BitBucket bietet kein fertiges Docker Image an, auf welchem CMake vorkonfiguriert ist, lässt einen aber andere Images vom Docker Hub einbinden.

Aber “nur wegen” einem “kleinen” C oder C++ Projekt habe ich keine Lust extern auch noch an Docker herumzudoktoren (zumindest heute nicht).

Die Frage ist daher:

Kann man nicht ein Standard-Image so “umkonfigurieren”, dass es CMake builds anstoßen kann?

Die Antwort lautet: Ja

Man muss nur bei jedem Build-Vorgang die nötigen Tools und Bibliotheken nachinstallieren. Und nachdem die BitBucket Images auf Ubuntu Linux basieren, reicht ein entsprechendes apt-get aus.

Die Lösung: apt-get install cmake

Liegt im Root eines BitBucket-Repositories eine Datei namens bitbucket-pipelines.yml mit korrektem Inhalt, startet nach jedem Code-Update die Build-Pipeline.
Kostenlos stehen einem im Monat 50 Minuten Build-Zeit zur Verfügung, wer mehr braucht, kann natürlich Münzen einwerfen.

Mein ursprüngliches yml Script für das GATE Projekt sah so aus:

 1image:
 2  name: atlassian/default-image:2
 3pipelines:
 4  default:
 5    - step:
 6        name: Build
 7        script:
 8          - apt-get update -qq
 9          - apt-get install cmake libasound2-dev v4l-utils libv4l-dev unixodbc-dev libgtk-3-dev -y --force-yes
10          - mkdir -p build-cmake
11          - cd build-cmake
12          - cmake ../src
13          - make

Das default-image:2 sollte benutzt werden, weil es auf Ubuntu 16.04 aufbaut. Fehlt dieser Eintrag, so landet man beim Image 1 mit Ubuntu 14.04 und dort ist CMake nur schwer veraltet nachinstallierbar und auch das nur nach dem Hinzufügen entsprechender Installationsquellen aus dem Netz.

Der Ablauf sieht in etwa so aus:

  • Wir aktualisieren apt auf den neuesten Stand und installieren dann CMake und alle anderen Entwicklungsbibliotheken für unsere Zwecke.
  • Wir erzeugen ein Unterverzeichnis, in dem CMake arbeiten können soll.
  • Wir wechseln in dieses Verzeichnis und führen dort cmake so aus, dass es die Dateien aus den Quellcodeverzeichnis abarbeitet. Beim GATE Projekt ist das /src und somit muss der relative Pfad ../src für CMake benutzt werden.
  • Danach wird einfach make aufgerufen, das die von CMake generierten Anweisungen ausführt.

That’s it!

Caches

Seit noch gar nicht so lange (Stand 2020) bewirbt BitBucket, dass es nun “Caches” unterstützt.
Während normalerweise jeder Build-Vorgang mit einem leeren Image beginnt, können mit aktivierten Caches die Ergebnisse eines Builds bzw. die Inhalte von bestimmten Verzeichnissen gesichert und beim folgenden Build weiterbenutzt werden.

Anfangs dachte ich mir:

Hey das ist DIE Lösung für das Nachinstallieren von Programmen.

Doch Fehlanzeige! Zwar kann man teilweise die apt Ergebnisse sichern, doch das Herunterladen dieser Datenmengen dauert genau so lange, wie wenn man die Installation gleich von vorne beginnt … zumindest bei meinen Paketen verhält es sich so.

Aber an einer anderen Stelle bewirken “Caches” kleine Wunder:
Und zwar bei den CMake und make Build-Verzeichnissen.

Die sind nur einige Megabytes groß und können daher relativ schnell gesichert und wiederhergestellt werden, aber die Ersparnis beim Kompilieren, wo nur noch Änderungen übersetzt werden müssen, ist enorm.

Mein finales GATE Script sieht daher so aus:

 1image:
 2  name: atlassian/default-image:2
 3pipelines:
 4  default:
 5    - step:
 6        name: Build
 7        caches:
 8          - buildjob
 9        script:
10          - rm -rf build-cmake/bin-output
11          - apt-get update -qq
12          - apt-get install cmake libasound2-dev v4l-utils libv4l-dev unixodbc-dev libgtk-3-dev -y --force-yes
13          - cmake --version          
14          - mkdir -p build-cmake
15          - cd build-cmake
16          - cmake ../src
17          - make
18          - cd ..
19        artifacts:
20          - build-cmake/bin-output/**
21definitions:
22  caches:
23    buildjob: build-cmake

Fazit

Ohne Caches braucht ein gesamter Build zwischen 3,5 und 4 Minuten. Davon etwa 30 Sekunden upload/download von Daten, etwa 30 Sekunden für apt, und der Rest sind CMake und make, also zwischen 2 und 3 Minuten.

Abhängig von der Menge der Änderungen dauert ein “inkrementeller” Build mit Caches aber ebenso nur noch 30 Sekunden.
Das liegt vor allem an der libreSSL. Diese wird von mir (außer bei halbjährlichen Updates) nicht geändert und macht den Großteil der Buildzeit aus.

Die GATE Sourcen sind da genügsamer und in etwa 30 Sekunden durchgeackert. Und da im kostenlosen Angebot nur 50 Minuten pro Monat für Buildpipelines inkludiert sind, macht sich der Cache durchaus “bezahlt”.

Abschließend wird innerhalb von CMAKE mit

1set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin-output CACHE PATH "Shared library target directory")
2set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin-output CACHE PATH "Executable target directory")
3set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin-output CACHE PATH "Static library target directory")

definiert, dass die Resultate im Verzeichnis bin-output untergebracht werden sollen. Dieses wandert dann ins Build-Artifactory und somit kann das Ergebnis eines Builds bequem als ZIP heruntergeladen werden.

Ursprünglich dachte ich

Ach was, das brauch ich doch nicht.

Doch nun gefällt mir der BitBucket Linux Build mehr und mehr, vor allem wenn ich von Extern mal schnell die aktuellste Version eines Tools für Linux brauche.

Jetzt fehlt mir noch ein Windows-Build … doch der ist (vorerst) hier nicht zu bekommen.

PS: Natürlich kann man mit make -j4 durch Parallelisierung auch die Build-Zeit reduzieren, doch da die Ressourcen von mehreren Usern gebracht werden, gibt es keine Garantie, dass schnell gebuildet wird. Die Reduktion des Build-Materials ist also immer eine gute Idee.


Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!