Привет, Хабр! Предлагаю вашему вниманию перевод статьи "Using GitHub Actions with C++ and CMake" о сборке проекта на C++ с использованием GitHub Actions и CMake автора Кристиана Адама.
Использование GitHub Actions с C++ и CMake
В этом посте я хочу показать файл конфигурации GitHub Actions для проекта C++, использующего CMake.
GitHub Actions это предоставляемая GitHub инфраструктура CI/CD. Сейчас GitHub Actions предлагает следующие виртуальные машины (runners):
Виртуальное окружение | Имя рабочего процесса YAML |
---|---|
Windows Server 2019 | windows-latest |
Ubuntu 18.04 | ubuntu-latest or ubuntu-18.04 |
Ubuntu 16.04 | ubuntu-16.04 |
macOS Catalina 10.15 | macos-latest |
Каждая виртуальная машина имеет одинаковые доступные аппаратные ресурсы:
- 2х ядерное CPU
- 7 Гб оперативной памяти
- 14 Гб на диске SSD
Каждое задание рабочего процесса может выполняться до 6 часов.
К сожалению, когда я включил GitHub Actions в проекте C++, мне предложили такой рабочий процесс:
./configuremakemake checkmake distcheck
Это немного не то, что можно использовать с CMake.
Hello World
Я хочу собрать традиционное тестовое приложение C++:
#include <iostream>int main(){ std::cout << "Hello world\n";}
Со следующим проектом CMake:
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 смотрите проект на GitHub.
Матрица сборки
Я начал со следующей матрицы сборки:
name: CMake Build Matrixon: [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++" }
Свежие CMake и Ninja
На странице установленного ПО виртуальных машин мы видим, что CMake есть везде, но в разных версиях:
Виртуальное окружение | Версия CMake |
---|---|
Windows Server 2019 | 3.16.0 |
Ubuntu 18.04 | 3.12.4 |
macOS Catalina 10.15 | 3.15.5 |
Это значит, что нужно будет ограничить минимальную версию CMake до 3.12 или обновить CMake.
CMake 3.16 поддерживает прекомпиляцию заголовков и Unity Builds, которые помогают сократить время сборки.
Поскольку у CMake и Ninja есть репозитории на GitHub, я решил скачать нужные релизы с GitHub.
Для написания скрипта я использовал CMake, потому что виртуальные машины по умолчанию используют свойственный им язык скриптов (bash для Linux и powershell для Windows). CMake умеет выполнять процессы, загружать файлы, извлекать архивы и делать еще много полезных вещей.
- 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()
Шаг настройки
Теперь, когда у меня есть CMake и Ninja, все, что мне нужно сделать, это настроить проект таким образом:
- 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()
Я установил переменные окружения CC
и
CXX
, а для MSVC мне пришлось выполнить скрипт
vcvars64.bat
, получить все переменные окружения и
установить их для выполняющегося скрипта CMake.
Шаг сборки
Шаг сборки включает в себя запуск CMake с параметром
--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()
Что бы увидеть скорость компиляции на разном виртуальном
окружении, я установил переменную NINJA_STATUS
.
Для переменных MSVC я использовал скрипт
environment_script_output.txt
, полученный на шаге
настройки.
Шаг запуска тестов
На этом шаге вызывается 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()
Шаги установки, упаковки и загрузки
Эти шаги включают запуск CMake с --install
,
последующий вызов CMake для создания архива 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 }}
Я не стал использовать CMake в качестве языка сценариев для простых вызовов CMake с параметрами, оболочки по умолчанию прекрасно с этим справляются.
Обработка релизов
Когда вы помечаете релиз в git, вы также хотите, чтобы артефакты сборки прикрепились к релизу:
git tag -a v1.0.0 -m "Release v1.0.0"git push origin v1.0.0
Ниже приведён код для этого, который сработает, если git refpath
содержит 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_urlpublish: 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
Это выглядит сложным, но это необходимо, так как
actions/create-release
можно вызвать однократно, иначе
это действие закончится ошибкой. Это обсуждается в issue #14
и issue #27.
Несмотря на то, что вы можете использовать рабочий процесс до 6
часов, токен secrets.GITHUB_TOKEN
действителен один
час. Вы можете создать личный токен или загрузить артефакты в
релиз вручную. Подробности в обсуждении сообщества
GitHub.
Заключение
Включить GitHub Actions в вашем проекте на CMake становится
проще, если создать файл
.github/workflows/build_cmake.yml
с содержимым из
build_cmake.yml.
Вы можете посмотреть GitHub Actions в моем проекте Hello World GitHub.
Оригинальный текст опубликован под лицензией CC BY 4.0.