I want to build against three compiler versions for a C and C++ project: gcc, gcc-8 and clang (for the C compiler), which should use g++, g++-8 and clang++ respectively for the C++ compiler.
That's 3 configurations total. I won't want to build with the product of all C and C++ compiler versions, i.e,. no gcc/g++-8, etc.
How can I specify a matrix with those three configurations, each which sets two variables?
Currently I'm using this (note that 2 OSes are specified, so it's 6 configs in total):
strategy:
matrix:
os: [ubuntu-16.04, ubuntu-latest]
cpp_compiler: [g++, g++-8, clang++]
include:
- c_compiler: gcc
- cpp_ompiler: g++-8
c_compiler: gcc-8
- cpp_compiler: clang++
c_compiler: clang
Essentially, the C++ compiler (cpp_compiler
) is used as the master version, and then include
is used in a hacky way to set c_compiler
based on the cpp_compiler
version, but there must be something better...
A matrix strategy lets you use variables in a single job definition to automatically create multiple job runs that are based on the combinations of the variables. For example, you can use a matrix strategy to test your code in multiple versions of a language or on multiple operating systems.
To run jobs sequentially, you can define dependencies on other jobs using the jobs. <job_id>. needs keyword. Each job runs in a runner environment specified by runs-on .
You can configure a GitHub Actions workflow to be triggered when an event occurs in your repository, such as a pull request being opened or an issue being created. Your workflow contains one or more jobs which can run in sequential order or in parallel.
With GitHub Actions, you can trigger CI/CD workflows and pipelines of webhooks from these apps (even something simple, like a chat app message, if you've integrated your chat app into your GitHub repository, of course).
Besides @jidicula's answer, which stores matrix
as an array
, you can also store it as a map
, this will make the workflow more readable.
So the following workflow should work prettier:
# simplified
strategy:
matrix:
os: [ubuntu-16.04, ubuntu-latest]
compiler: [ {cpp: g++, c: gcc}, {cpp: g++-8, c: gcc-8}, {cpp: clang++, c: clang} ]
steps:
- name: Compile with C++ compiler
run: ${{ matrix.compiler.cpp }} source.cpp
- name: Compile with C compiler
run: ${{ matrix.compiler.c }} source.c
When this workflow is triggered, it will execute 6 times in parallel with different matrix
.
1: matrix.os == ubuntu-16.04, matrix.compiler.cpp == g++, matrix.compiler.c == gcc
2: matrix.os == ubuntu-16.04, matrix.compiler.cpp == g++-8, matrix.compiler.c == gcc-8
3: matrix.os == ubuntu-16.04, matrix.compiler.cpp == clang++, matrix.compiler.c == clang
4: matrix.os == ubuntu-latest, matrix.compiler.cpp == g++, matrix.compiler.c == gcc
5: matrix.os == ubuntu-latest, matrix.compiler.cpp == g++-8, matrix.compiler.c == gcc-8
6: matrix.os == ubuntu-latest, matrix.compiler.cpp == clang++, matrix.compiler.c == clang
However, whether it is an array
or a map
, the syntax is currently undocumented, though as of a certain point in time the syntax checker no longer flags it as an error. So all of this may change in the future.
I just tried this out and it turns out you can nest arrays in a build matrix, but it's undocumented. Therefore, you can create an array of arrays of compilers in your job.matrix
. Let's call it compiler-version
: compiler-version: [[g++, gcc], [g++-8, gcc-8], [clang++, clang]]
. Then you can access each value in each element of the compiler-version
array with zero-indexing: matrix.compiler-version[0]
for the C++ compiler, and matrix.compiler-version[1]
its C counterpart.
Your workflow would become:
strategy:
matrix:
os: [ubuntu-16.04, ubuntu-latest]
compiler-version: [[g++, gcc], [g++-8, gcc-8], [clang++, clang]]
steps:
# .... checkout steps
- name: Compile file with C++ compiler
env:
COMPILER: ${{ matrix.compiler-version[0] }}
run: |
$COMPILER file.c
./a.out
- name: Compile file with C compiler
env:
COMPILER: ${{ matrix.compiler-version[1] }}
run: |
$COMPILER file.c
./a.out
I've worked up a minimal example with a matrix of 2 Python versions and 2 Ubuntu versions, where 3.7 is paired with Ubuntu-18.04, and 3.8 is paired with Ubuntu-20.04. You can see the workflow file here and its corresponding run here.
This solution gets around the awkwardness of having to define your C++ options in 2 places in the workflow file. Even though it's undocumented, I assume it's stable since GitHub Actions context objects are (or at least are treated like) JavaScript objects, so we're really just defining array literals as the matrix object attributes and accessing their values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With