Während früher ein Entwickler auf seiner eigenen Maschine nur zur Mond- oder Sonnenwende ein neues Software-Kompilat erzeugen wollte, spricht man heute von Continuous Integration und Continous Delivery (kurz CI und CD) und meint damit meist, dass zu jeder Zeit ein Softwarestand lieferfertig gemacht werden können soll.
Azure-DevOps oder Team-Foundation Server, TeamCity, Jenkins, GitLab … so heißen einige der bekannten Build-Server, mit denen man automatisiert das Kompilieren von Quellcodes anstoßen kann. Sie alle bringen ihre Vorteile am Einsatzort zum Tragen und helfen dabei, die Auslieferung von Software zu automatisieren und auch zu standardisieren.
Ich möchte aber für meine Sourcen auf meinen minimalistischen Systemen keine aufwändigen Dienste wie Datenbanken und Webserver installieren, und suche daher eine Möglichkeit, Builds ohne Zusatztools automatisch anzustarten.
Die Build-Automations-Scripts können auf einem Windows Server mit Docker Unterstützung mitlaufen. Das Beispiel sollte allerdings auch mit Docker Desktop funktionieren.
Wir brauchen dafür eine Docker-kompatible Visual Studio Umgebung für C++ Projekte. Natürlich sind auch andere Umgebungen möglich, auf diese wird aber hier nicht eingegangen.
Dann muss GIT für Windows auf dem Server installiert werden.
Und am Ende benötigen wir einen Ordner, in dem die Projekte heruntergeladen und gebaut werden können.
Ab Visual Studio 2017 ist das Bauen in Docker offiziell unterstützt. Die Installation wird vom VS-Build-Tools Installer eingeleitet, der unter folgenden Adressen verfügbar ist:
Dem Installer kann per Kommandozeile aufgetragen werden, welche Teilkomponenten
er laden und installieren soll.
Für VC++ sind die Workload-Pakete Microsoft.VisualStudio.Workload.MSBuildTools
und Microsoft.VisualStudio.Workload.VCTools
erforderlich.
Zusätzlich wollen wir, dass auch “empfohlene” Zusatzpakete installiert werden,
womit wir die Windows SDK
und CMake erhalten.
Das Dockerfile
hierfür sieht wie folgt aus:
1# escape=` 2 3# Select the required Windows Server Release: 4#FROM mcr.microsoft.com/windows/servercore:ltsc2016 5#FROM mcr.microsoft.com/windows/servercore:ltsc2019 6FROM mcr.microsoft.com/windows/servercore:ltsc2022 7 8SHELL ["cmd", "/S", "/C"] 9 10RUN ` 11 curl -SL --output vs_buildtools.exe ` 12 https://aka.ms/vs/15/release/vs_buildtools.exe ` 13 && (start /w ` 14 vs_buildtools.exe --quiet --wait --norestart --nocache ` 15 --installPath ` 16 "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\BuildTools" ` 17 --add Microsoft.VisualStudio.Workload.MSBuildTools ` 18 --add Microsoft.VisualStudio.Workload.VCTools ` 19 --includeRecommended ` 20 || IF "%ERRORLEVEL%"=="3010" EXIT 0) ` 21 && del /q vs_buildtools.exe 22 23ENTRYPOINT [ ` 24"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\Common7\\Tools\\VsDevCmd.bat", ` 25"&&", "cmd.exe", "/S", "/C"]
Mit dem Aufruf von
docker build -t msvc-build:2017 -f Dockerfile .
im Verzeichnis der Datei
wird das Docker-Image erstellt und mit dem TAG msvc-build:2017
abgelegt.
Dockerfile
lädt den Visual Studio 2017 Installer herunter, startet ihn über das
start
Kommando und wartet bis die Installation fertig ist.
Als ENTRYPOINT
wird das Visual Studio-Environment Script geladen, damit alle
notwendigen Tools in die Environment-Variablen geladen werden.
Den zu bauenden Quellcodes sollte ein Script beiliegen, das den gesamten
Kompiliervorgang verwalten kann.
Wichtig ist dabei zu wissen, wo das Build-Script am Ende seine
Daten ablegt, denn von der Stelle muss das Folgescript die
fertigen Binaries abholen können.
Am einfachsten ist es, im Stammverzeichnis des Projektes eine
Datei namens build.bat
abzulegen.
Wer eine Visual Studio Solution (*.sln
) mit den Quellen in GIT eingecheckt
hat, braucht nur msbuild
in der Batch Datei aufzurufen.
1msbuild .\my-project.sln /t:Rebuild /p:configuration=release /p:platform=x64 /m
Die Binärdateien sollten dann in .\x64\Release
ausgegeben werden.
Im Falle von 32-Bit Kompilaten, liegen sie in .\Release
, wenn sie wie folgt
erzeugt werden:
1msbuild .\my-project.sln /t:Rebuild /p:configuration=release /p:platform=win32 /m
In CMakeLists.txt
lässt sich per Variable definieren, wohin Binaries
am Ende geschrieben werden sollen:
1set(MY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/output" ) 2 3set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${MY_OUTPUT_PATH}" 4 CACHE PATH "Shared library target directory") 5set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${MY_OUTPUT_PATH}" 6 CACHE PATH "Static library target directory") 7set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${MY_OUTPUT_PATH}" 8 CACHE PATH "Executable target directory")
Wenn wir CMake im Unterverzeichnis build
des Source-Verzeichnis arbeiten
lassen, werden dann die finalen Binärdateien in .\build\output\
zu finden sein.
Auf dem Windows Server wird nun für jedes Projekt ein Server-Build Script angelegt, das wie folgt vorgeht:
Mein Beispiel Script sieht wie folgt aus:
1SET GIT_URL=https://user:password@git-server.tld/path-to/project.git 2SET DOCKER_IMAGE=msvc-build:2017 3SET WORKDIR=%CD% 4SET BUILD_ID=%RANDOM% 5SET BUILD_DIR=%WORKDIR%\%BUILD_ID% 6SET OUTPUT_ID=%DATE:~-4%-%DATE:~-10,2%-%DATE:~-7,2%_%TIME:~0,2%-%TIME:~3,2%-%TIME:~6,2%_%BUILD_ID% 7SET OUTPUT_BASE=%WORKDIR%\artifacts 8SET OUTPUT_DIR="%OUTPUT_BASE%\%OUTPUT_ID%" 9DEL /F /S /Q %BUILD_DIR% 10MKDIR %BUILD_DIR% 11MKDIR %OUTPUT_BASE% 12MKDIR %OUTPUT_DIR% 13CHDIR %BUILD_DIR% 14 15git clone %GIT_URL% . 16 17docker run -v %BUILD_DIR%:c:\build %DOCKER_IMAGE% c:\build\build.bat 18 19tar -czvf %OUTPUT_DIR%\artifacts.tgz -C %BUILD_DIR%\build\output ./*.* 20 21CHDIR .. 22DEL /F /S /Q %BUILD_DIR%
Ein Gitlab Server verwaltet GIT Repositories und weitere registrierte Build-Server (genannt “Runner”), auf denen der Gitlab Runner Dienst installiert ist. Jeder Runner wird durch ein TAG (eine Textmarke) adressiert und stellt eine bestimmte Umgebung (wie Docker, oder eine Shell) bereit.
In einer YAML Datei werden dann pro Projekt Details der Umgebung und ein Build-Script festgelegt, das bei einem Build auf einem Runner ausgeführt werden soll.
Gitlab Projekte können eine CI (Continuous Integration) YAML Datei namens
.gitlab-ci.yml
enthalten, die die Buildumgebung und notwendige Schritte
beinhalten.
Der Buildprozess (auch Build Pipeline genannt) läuft dann so ab:
git clone
) die Quellen herunter
und startet die Umgebung (z.B. Docker) um darin das Build-Script auszuführen,
das im CI-YAML definiert ist (z.B: CMake ausführen).1# Liste von erlaubten "build stages" 2# "Stages" beinhalten "Jobs", die hintereinander ausgeführt 3# werden. Wenn ein Job fehlschlägt, wird die Pipeline 4# abgebrochen und nachfolgende Stages werden standardgemäß 5# nicht mehr ausgeführt. 6stages: 7 - "build" 8 9# Ein "Job" in der Stage namens "build", der auf einem Runner 10# ausgeführt werden soll, der mit dem TAG "my_runner_tag" 11# registriert wurde. Er soll das Docker Image mit dem Pfad 12# "my_docker_image_path:1.2" herunterladen und das nachfolgende 13# Script darin ausführen. 14# Am Ende wird der Inhalt des Unterverzeichnis "build-output/" 15# als Artefakt auf den Gitlab Server hochgeladen 16my_job1: 17 stage: "build" 18 tags: 19 - "my_runner_tag" 20 image: "my_docker_image_path:1.2" 21 script: 22 # scripts werden in dem Verzeichnis ausgeführt, 23 # in dem "git clone" das Projekt heruntergeladen hat. 24 - echo "Starting" 25 - make -j4 26 - echo "Finished" 27 artifacts: 28 paths: 29 - "build-output/*"
Fortsetzung folgt
opengate.at
.