OpenGATE Retro Blog |
Build Server ScriptingEinleitungWä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. Ziele
graph LR
S[Start
Server Script] subgraph Shared Folder between Host and Docker G(GIT Download) SS((Source Build Script)) D(Docker Container) A(Store Artifacts) end F[Finish Server Script] S --> G G --> D D --> A A --> F D --> SS SS --> D Windows Server ImplementierungVoraussetzungenDie 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. Docker SetupAb 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. Das 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
Als Source Build ScriptDen zu bauenden Quellcodes sollte ein Script beiliegen, das den gesamten
Kompiliervorgang verwalten kann. Am einfachsten ist es, im Stammverzeichnis des Projektes eine
Datei namens Beispiel Visual Studio SolutionWer eine Visual Studio Solution ( 1msbuild .\my-project.sln /t:Rebuild /p:configuration=release /p:platform=x64 /m
Die Binärdateien sollten dann in Im Falle von 32-Bit Kompilaten, liegen sie in 1msbuild .\my-project.sln /t:Rebuild /p:configuration=release /p:platform=win32 /m
Beispiel CMAKEIn 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 Server Build ScriptAuf 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% Buildserver ProdukteGitLab CIEin 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 BuildprozessGitlab Projekte können eine CI (Continuous Integration) YAML Datei namens
Der Buildprozess (auch Build Pipeline genannt) läuft dann so ab:
flowchart LR
subgraph GIT[GITLAB Project]
PROJECT[(GITLAB
Project Repository)] YAML[[.gitlab_ci.yml]] YAMLARTIFACT(YAML artifacts: section) subgraph BUILD[Build Process Definition] YAMLSCRIPT(YAML scripts: section) SCRIPTS[[other_build_scripts.sh]] CMAKE[[CMakeLists.txt]] SOURCES[[Sources *.h *.hpp *.c *.cpp]] end end subgraph CI Runner RUNNER((Gitlab Runner Service)) DOCKER(Docker Container) end GITLAB[(GITLAB Artifacts)] PROJECT --- YAML YAML --- YAMLSCRIPT YAML --- YAMLARTIFACT YAMLSCRIPT --script: cmake --build--> CMAKE YAMLSCRIPT --script: ./run.sh--> SCRIPTS SCRIPTS --compile--> SOURCES CMAKE --compile--> SOURCES PROJECT --git clone--> RUNNER RUNNER --execute YAML scripts--> DOCKER DOCKER --extract artifacts--> YAMLARTIFACT YAMLARTIFACT --Upload Build Results---> GITLAB .gitlab-ci.yml Beispieldatei1# 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 |