GitHub Action
cmake swiss army knife
ghaction-cmake is a github action for projects that use cmake. By default, it builds, tests and installs the project - but it can as easily run linters, tests with coverage, valgrind or sanitizers, by using presets.
ghaction-cmake runs in phases:
- setup: optionally install dependencies and go to a specified directory.
- cmake: run cmake in an empty directory, pointing to the source directory, with all other arguments appended. This guarantees that out-of-tree builds work.
- build: customizable,
make VERBOSE=1
by default (build commands are shown). - test: customizable,
ctest --output-on-failure .
by default. - post: customizable, empty by default.
Set a preset, more information on the Presets section below.
- Phase: can changes the default command of any number of phases.
Use this directory as the source dir for cmake. Mostly used when the cmake project is in a subdirectory of the repository.
- Phase: setup
- Preset behavior: unaffected.
Custom command to run before dependencies are installed. Use this to add third-party Debian repositories or increment the setup phase in other ways.
- Phase: setup
- Preset behavior: unaffected.
Project dependencies as Debian packages to install in the container, separated by spaces.
- Phase: setup
- Preset behavior: unaffected.
Flags for cmake. -DSOME_OPTION=On
, for instance, to pass an option
to CMakeLists.txt.
- Phase: cmake
- Preset behavior: most presets append to this input.
Custom test command. Defaults to make VERBOSE=1
.
- Phase: build
- Preset behavior: some presets change or remove the default build command.
Custom test command. Defaults to ctest --output-on-failure .
if no preset is used.
- Phase: test
- Preset behavior: some presets change or remove the default test command.
Custom command to run after tests. Empty by default, if no preset is used.
- Phase: post
- Preset behavior: some presets add a default post command.
cmake
is a very versatile tool that can do a lot of different things given
the appropriate arguments. To make matrix builds easier, ghaction-cmake
provides presets that configure those options for specific modes.
The available presets are:
-
cppcheck: run cppcheck static analysis.
- cmake: append
-DCMAKE_C/CXX_CPPCHECK=cppcheck
tocmakeflags
. - test: clear default.
- cmake: append
-
iwyu: run include-what-you-use static analysis.
- cmake: append
-DCMAKE_C/CXX_INCLUDE_WHAT_YOU_USE=iwyu
tocmakeflags
. - test: clear default.
- cmake: append
-
install: test installation.
- cmake: append
'-DCMAKE_INSTALL_PREFIX'
tocmakeflags
. - test: use
make install
as a test. - post: use
find
to show all installed files.
- cmake: append
-
clang-tidy: run clang-tidy static analysis.
- cmake: append
-DCMAKE_C/CXX_CLANG_TIDY=clang-tidy
tocmakeflags
. - test: clear default.
- cmake: append
-
clang-sanitize-<sanitizer>: compile with one of the clang sanitizers and run the tests.
- cmake: append
-DCMAKE_C/CXX_COMPILER=clang/clang++ -DCMAKE_C/CXX_FLAGS=-fno-omit-frame-pointer -fsanitize=<sanitizer>
tocmakeflags
.
- cmake: append
-
gcc-sanitize-<sanitizer>: compile with one of the gcc sanitizers and run the tests.
- cmake: append
-DCMAKE_C/CXX_COMPILER=gcc/g++ -DCMAKE_C/CXX_FLAGS=-fno-omit-frame-pointer -fsanitize=<sanitizer>
tocmakeflags
.
- cmake: append
-
valgrind: run the tests with valgrind.
- test: set default test phase to
ctest -DExperimentalMemCheck --output-on-failure .
- test: set default test phase to
-
cpack: runs cpack after the build.
- test: cpack
-
coverage: runs the tests with coverage.
- cmake: append
-DCMAKE_C/CXX_FLAGS=--coverage
tocmakeflags
- post: set default post phase to run
lcov with
lcov -c -d . -o lcov.info
This preset works well with github actions that upload coverage data results to online services like codecov and coveralls. The example below shows how that can be done.
- cmake: append
The table below summarizes the changes specific to each preset:
Preset | cmake | test | post |
---|---|---|---|
cppcheck | -DCMAKE_C/CXX_CPPCHECK=cppcheck |
(delete) | |
iwyu | -DCMAKE_C/CXX_INCLUDE_WHAT_YOU_USE=iwyu |
(delete) | |
install | -DCMAKE_INSTALL_PREFIX=/tmp/_install |
make install |
find /tmp_install -type f |
clang‑tidy | -DCMAKE_C/CXX_CLANG_TIDY=clang-tidy |
(delete) | |
clang‑sanitize‑<sanitizer> |
-DCMAKE_C/CXX_COMPILER=clang/clang++ -DCMAKE_C/CXX_FLAGS=-fno-omit-frame-pointer -fsanitize=<sanitizer> |
||
valgrind | -DExperimentalMemCheck |
||
cpack | cpack |
||
coverage | -DCMAKE_C/CXX_FLAGS=--coverage |
lcov -c -d . -o lcov.info |
Keep in mind that presets override the defaults, and are overriden by
the other more specific inputs build_command
, test_command
and
post_command
.
The workflow below shows how to use presets in a matrix job:
---
name: CI
on: [push, pull_request]
jobs:
# Regular C build with two compilers, using the environment:
build_using_compiler_in_environment:
strategy:
matrix:
compiler:
- [gcc, g++]
- [clang, clang++]
runs-on: ubuntu-latest
# We can use cmakeflags for this, or we can just use
# regular environment variables, as they are already
# supported by github actions:
env:
- CC: ${{ matrix.compiler[0] }}
- CXX: ${{ matrix.compiler[1] }}
steps:
- uses: actions/checkout@v2
- uses: docker://lpenz/ghaction-cmake:0.16
# Regular C build with two compilers, using cmakeflags:
build_using_compiler_in_cmakeflags:
strategy:
matrix:
compiler:
- gcc
- clang
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# This examples uses the appropriate cmakeflags
- uses: docker://lpenz/ghaction-cmake:0.16
with:
cmakeflags: ${{ format('-DCMAKE_C_COMPILER={0}', matrix.compiler) }}
# Coverage with codecov:
codecov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker://lpenz/ghaction-cmake:0.16
with:
preset: coverage
# ghaction-cmake works well with the github action
# provided by codecov:
- uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true
# Coverage with coveralls:
coveralls:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker://lpenz/ghaction-cmake:0.16
with:
preset: coverage
# ghaction-cmake works well with the github action
# provided by coveralls if you pass path-to-lcov:
- uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: lcov.info
# Static analyzers:
linters:
strategy:
matrix:
preset: [ cppcheck, iwyu, clang-tidy ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker://lpenz/ghaction-cmake:0.16
with:
preset: ${{ matrix.preset }}
# Tests with various sanitizers and valgrind:
test:
strategy:
matrix:
preset:
- clang-sanitize-address
- clang-sanitize-memory
- clang-sanitize-undefined
- clang-sanitize-dataflow
- clang-sanitize-safe-stack
- valgrind
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker://lpenz/ghaction-cmake:0.16
with:
preset: ${{ matrix.preset }}
# Test installation:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker://lpenz/ghaction-cmake:0.16
with:
preset: install
Note that the file above splits static analyzers from sanitizers, but they can actually be in the same matrix job, as the rest of the parameters is the same.
This github action is actually a docker image that can be used locally or even in travis-ci. To do that, first download the image from docker hub:
docker pull lpenz/ghaction-cmake:0.16
Then, run a container in the project's directory, for instance:
docker run --rm -t -u "$UID" -w "$PWD" -v "${PWD}:${PWD}" -e INPUT_PRESET=valgrind lpenz/ghaction-cmake:0.16
It's worth pointing out that action parameters are passed as
upper case environment variables, prefixed with INPUT_
.
The following .travis.yml
runs the same thing in travis-ci:
---
language: generic
jobs:
include:
- install: docker pull lpenz/ghaction-cmake:0.16
- script: docker run --rm -t -u "$UID" -w "$PWD" -v "${PWD}:${PWD}" -e INPUT_PRESET=valgrind lpenz/ghaction-cmake:0.16