Dans cet article, je vais fournir un fichier yaml de configuration GitHub Actions pour les projets C++ utilisant CMake.
GitHub Actions est une infrastructure CI/CD fournie par GitHub. GitHub Actions Propose actuellement les machines virtuelles suivantes (exécuteurs) :
Environnement virtuel | Étiquette de flux de travail YAML |
---|---|
Windows Server 2019 | windows-latest |
Ubuntu 18.04 | ubuntu-latest ou ubuntu-18.04 |
Ubuntu 16.04 | ubuntu-16.04 |
macOS Catalina 10.15 | macOS-latest |
Chaque machine virtuelle dispose des mêmes ressources matérielles :
- Processeur à 2 cœurs
- 7 Go de mémoire RAM
- 14 Go d’espace disque SSD
Chaque tâche d’un workflow peut s’exécuter jusqu’à 6 heures d’exécution.
Malheureusement, lorsque j’ai activé GitHub Actions sur un projet C++, ce flux de travail m’a été présenté :
./configure make make check make distcheck
Ce n’est pas quelque chose que vous pouvez utiliser avec CMake cependant
Hello world
Cistian adam a construit le programme C++ hello world suivant :
#include <iostream> int main() { std::cout << "Hello world\n"; }
Avec le projet CMake suivant :
cmake_minimum_required(VERSION 3.16) project(main) add_executable(main main.cpp) install(TARGETS main) enable_testing() add_test(NAME main COMMAND main)
TL; DR voir le projet sur GitHub.
Matrice de construction
Cistian adam a commencé avec la matrice de construction suivante :
name: CMake Build Matrix on: [push] jobs: build: name: ${ { matrix.config.name } } runs-on: ${ { matrix.config.os } } strategy: fail-fast: false matrix: config: - { name: "Windows Latest MSVC", artifact: "Windows-MSVC.tar.xz", os: windows-latest, build_type: "Release", cc: "cl", cxx: "cl", environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat" } - { name: "Windows Latest MinGW", artifact: "Windows-MinGW.tar.xz", os: windows-latest, build_type: "Release", cc: "gcc", cxx: "g++" } - { name: "Ubuntu Latest GCC", artifact: "Linux.tar.xz", os: ubuntu-latest, build_type: "Release", cc: "gcc", cxx: "g++" } - { name: "macOS Latest Clang", artifact: "macOS.tar.xz", os: macos-latest, build_type: "Release", cc: "clang", cxx: "clang++" }
Latest CMake and Ninja
In the software installed on the runners page we can see that CMake is installed on all runners, but with different versions:
Virtual environment | CMake Version |
---|---|
Windows Server 2019 | 3.16.0 |
Ubuntu 18.04 | 3.12.4 |
macOS Catalina 10.15 | 3.15.5 |
This would mean that one would have to limit the minimum CMake version to 3.12, or upgrade CMake.
CMake 3.16 comes with support for Precompile Headers and Unity Builds, which help reducing build times.
Since CMake and Ninja have GitHub Releases, I decided to download those GitHub releases.
I used CMake as a scripting language, since the default scripting language for runners is different (bash, and powershell). CMake can execute processes, download files, extract archives.
- name: Download Ninja and CMake id: cmake_and_ninja shell: cmake -P {0} run: | set(ninja_version "1.9.0") set(cmake_version "3.16.2") message(STATUS "Using host CMake version: ${CMAKE_VERSION}") if ("${ { runner.os } }" STREQUAL "Windows") set(ninja_suffix "win.zip") set(cmake_suffix "win64-x64.zip") set(cmake_dir "cmake-${cmake_version}-win64-x64/bin") elseif ("${ { runner.os } }" STREQUAL "Linux") set(ninja_suffix "linux.zip") set(cmake_suffix "Linux-x86_64.tar.gz") set(cmake_dir "cmake-${cmake_version}-Linux-x86_64/bin") elseif ("${ { runner.os } }" STREQUAL "macOS") set(ninja_suffix "mac.zip") set(cmake_suffix "Darwin-x86_64.tar.gz") set(cmake_dir "cmake-${cmake_version}-Darwin-x86_64/CMake.app/Contents/bin") endif() set(ninja_url "https://github.com/ninja-build/ninja/releases/download/v${ninja_version}/ninja-${ninja_suffix}") file(DOWNLOAD "${ninja_url}" ./ninja.zip SHOW_PROGRESS) execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./ninja.zip) set(cmake_url "https://github.com/Kitware/CMake/releases/download/v${cmake_version}/cmake-${cmake_version}-${cmake_suffix}") file(DOWNLOAD "${cmake_url}" ./cmake.zip SHOW_PROGRESS) execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./cmake.zip) # Save the path for other steps file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/${cmake_dir}" cmake_dir) message("::set-output name=cmake_dir::${cmake_dir}") if (NOT "${ { runner.os } }" STREQUAL "Windows") execute_process( COMMAND chmod +x ninja COMMAND chmod +x ${cmake_dir}/cmake ) endif()
Configure step
Now that I have CMake and Ninja, all I have to do is configure the project like this:
- name: Configure shell: cmake -P {0} run: | set(ENV{CC} ${ { matrix.config.cc } }) set(ENV{CXX} ${ { matrix.config.cxx } }) if ("${ { runner.os } }" STREQUAL "Windows" AND NOT "x${ { matrix.config.environment_script } }" STREQUAL "x") execute_process( COMMAND "${ { matrix.config.environment_script } }" && set OUTPUT_FILE environment_script_output.txt ) file(STRINGS environment_script_output.txt output_lines) foreach(line IN LISTS output_lines) if (line MATCHES "^([a-zA-Z0-9_-]+)=(.*)$") set(ENV{${CMAKE_MATCH_1} } "${CMAKE_MATCH_2}") endif() endforeach() endif() file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/ninja" ninja_program) execute_process( COMMAND ${ { steps.cmake_and_ninja.outputs.cmake_dir } }/cmake -S . -B build -D CMAKE_BUILD_TYPE=${ { matrix.config.build_type } } -G Ninja -D CMAKE_MAKE_PROGRAM=${ninja_program} RESULT_VARIABLE result ) if (NOT result EQUAL 0) message(FATAL_ERROR "Bad exit status") endif()
Cistian adam a définit les variables d’environnement et pour MSVC, il a dû exécuter le script, pour obtenir toutes les variables d’environnement et les définir pour le script d’exécution de CMake.CCCXXvcvars64.bat
Étape de construction
L’étape de génération implique l’exécution du paramètre CMake avec :--build
- name: Build shell: cmake -P {0} run: | set(ENV{NINJA_STATUS} "[%f/%t %o/sec] ") if ("${ { runner.os } }" STREQUAL "Windows" AND NOT "x${ { matrix.config.environment_script } }" STREQUAL "x") file(STRINGS environment_script_output.txt output_lines) foreach(line IN LISTS output_lines) if (line MATCHES "^([a-zA-Z0-9_-]+)=(.*)$") set(ENV{${CMAKE_MATCH_1} } "${CMAKE_MATCH_2}") endif() endforeach() endif() execute_process( COMMAND ${ { steps.cmake_and_ninja.outputs.cmake_dir } }/cmake --build build RESULT_VARIABLE result ) if (NOT result EQUAL 0) message(FATAL_ERROR "Bad exit status") endif()
Cistian adam a définit la variable, pour voir à quelle vitesse la compilation est dans les coureurs respectifs.NINJA_STATUS
Pour MSVC,il a réutilisé le script de l’étape Configurer.environment_script_output.txt
Étape Exécuter les tests
Cette étape appelle avec le nombre de cœurs passés en argument :ctest
-j
- name: Run tests shell: cmake -P {0} run: | include(ProcessorCount) ProcessorCount(N) execute_process( COMMAND ${ { steps.cmake_and_ninja.outputs.cmake_dir } }/ctest -j ${N} WORKING_DIRECTORY build RESULT_VARIABLE result ) if (NOT result EQUAL 0) message(FATAL_ERROR "Running tests failed!") endif()
Étapes d’installation, d’emballage et de téléchargement
Cette étape implique l’exécution de CMake avec , puis la création d’une archive avec CMake, et en le téléchargeant en tant qu’artefact de build.--install
tar.xz
- name: Install Strip run: ${ { steps.cmake_and_ninja.outputs.cmake_dir } }/cmake --install build --prefix instdir --strip - name: Pack working-directory: instdir run: ${ { steps.cmake_and_ninja.outputs.cmake_dir } }/cmake -E tar cJfv ../${ { matrix.config.artifact } } . - name: Upload uses: actions/upload-artifact@v1 with: path: ./${ { matrix.config.artifact } } name: ${ { matrix.config.artifact } }
il n’a pas utilisé CMake comme langage de script, car cela implique simplement d’appeler CMake avec des paramètres, et le les shells par défaut peuvent gérer cela
Gestion des rejets
Lorsque vous marquez une version dans git, vous souhaitez également que les artefacts de build soient promus en tant que versions :
git tag -a v1.0.0 -m "Release v1.0.0" git push origin v1.0.0
Le code pour ce faire est ci-dessous, est déclenché si le refpath git contient :tags/v
release: if: contains(github.ref, 'tags/v') runs-on: ubuntu-latest needs: build steps: - name: Create Release id: create_release uses: actions/create-release@v1.0.0 env: GITHUB_TOKEN: ${ { secrets.GITHUB_TOKEN } } with: tag_name: ${ { github.ref } } release_name: Release ${ { github.ref } } draft: false prerelease: false - name: Store Release url run: | echo "${ { steps.create_release.outputs.upload_url } }" > ./upload_url - uses: actions/upload-artifact@v1 with: path: ./upload_url name: upload_url publish: if: contains(github.ref, 'tags/v') name: ${ { matrix.config.name } } runs-on: ${ { matrix.config.os } } strategy: fail-fast: false matrix: config: - { name: "Windows Latest MSVC", artifact: "Windows-MSVC.tar.xz", os: ubuntu-latest } - { name: "Windows Latest MinGW", artifact: "Windows-MinGW.tar.xz", os: ubuntu-latest } - { name: "Ubuntu Latest GCC", artifact: "Linux.tar.xz", os: ubuntu-latest } - { name: "macOS Latest Clang", artifact: "macOS.tar.xz", os: ubuntu-latest } needs: release steps: - name: Download artifact uses: actions/download-artifact@v1 with: name: ${ { matrix.config.artifact } } path: ./ - name: Download URL uses: actions/download-artifact@v1 with: name: upload_url path: ./ - id: set_upload_url run: | upload_url=`cat ./upload_url` echo ::set-output name=upload_url::$upload_url - name: Upload to Release id: upload_to_release uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${ { secrets.GITHUB_TOKEN } } with: upload_url: ${ { steps.set_upload_url.outputs.upload_url } } asset_path: ./${ { matrix.config.artifact } } asset_name: ${ { matrix.config.artifact } } asset_content_type: application/x-gtar
This looks complicated, but it’s needed since needs to be called only once, otherwise it will fail. See issue #14, issue #27 for more information.actions/create-release
Même si vous pouvez utiliser un flux de travail pendant 6 heures, le expire dans une heure. Vous pouvez soit créer un jeton personnel, soit Téléchargez les artefacts manuellement dans la version. Voir cette communauté GitHub pour plus d’informations.secrets.GITHUB_TOKEN
Fermeture
L’activation de GitHub Actions sur votre projet CMake est aussi simple que de créer un fichier avec le contenu de .github/workflows/build_cmake.yml
build_cmake.yml.