Русский
Русский
English
Статистика
Реклама

Из песочницы Использование GitHub Actions с C и CMake

Привет, Хабр! Предлагаю вашему вниманию перевод статьи "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.

Источник: habr.com
К списку статей
Опубликовано: 24.06.2020 14:16:49
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

C++

Git

Github

Программирование

Cmake

Github actions

Категории

Последние комментарии

© 2006-2020, personeltest.ru