Cristian Adam

Speeding up C++ GitHub Actions using ccache

In my previous post Using GitHub Actions with C++ and CMake I have provided a GitHub Actions yaml configuration file for C++ projects using CMake.

Building a project on GitHub Actions means always a build from scratch, for any given change, big or small. This takes time and wastes resources unnecessarily.

GitHub provides a way of caching dependencies to speed up workflows. The total size of cached files per repository is 2 GiB.

By having a look at the examples of various programming languages we can see that this is meant to cache package manager dependencies e.g. pip for python, npn for node, or gradle for java.

But, as it turns out, the caching mechanism can be used to cache compilation artifacts.

ccache

ccache (or “Ccache”) is a compiler cache. It speeds up recompilation by caching previous compilations and detecting when the same compilation is being done again. Supported languages are C, C++, Objective-C and Objective-C++.

The following yaml file excerpt will enable ccache support for GitHub Actions:

- name: Prepare ccache timestamp
    id: ccache_cache_timestamp
    shell: cmake -P {0}
    run: |
    string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
    message("::set-output name=timestamp::${current_date}")

- name: ccache cache files
    uses: actions/cache@v1.1.0
    with:
    path: .ccache
    key: ${ { matrix.config.name } }-ccache-${ { steps.ccache_cache_timestamp.outputs.timestamp } }
    restore-keys: |
        ${ { matrix.config.name } }-ccache-

This makes sure that for every build the GitHub Actions cache key is unique. It will restore the latest tar file containing the .ccache folder for the current configuration, and and the end of the job it will store the updated .ccache folder in a new tar file.

Using ccache with CMake

In the configure step one only needs to pass:

-D CMAKE_C_COMPILER_LAUNCHER=ccache
-D CMAKE_CXX_COMPILER_LAUNCHER=ccache

Before building the project I am configuring ccache via environment variables like this:

file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}" ccache_basedir)
set(ENV{CCACHE_BASEDIR} "${ccache_basedir}")
set(ENV{CCACHE_DIR} "${ccache_basedir}/.ccache")
set(ENV{CCACHE_COMPRESS} "true")
set(ENV{CCACHE_COMPRESSLEVEL} "6")
set(ENV{CCACHE_MAXSIZE} "400M")
if ("${ { matrix.config.cxx } }" STREQUAL "cl")
    set(ENV{CCACHE_MAXSIZE} "600M")
endif()

This will ensure that the maximum size of the cache will be 400 MiB, will use compression, and the paths will always be relative to the build directory.

ccache statistics are zeroed before starting the build (ccache -z), and displayed after the build (ccache -s).

Getting ccache

ccache project doesn’t have any binary releases on their github page, like CMake or ninja.

One could use brew to install ccache on macOS, apt get to install ccache on Ubuntu, but what about Windows?

I have my own ccache fork, which has three commits over the official ccache:

  1. CMake build system - to build on Windows
  2. GitHub Actions yaml file - for providing binary releases
  3. Visual C++ (alpha) support - for having a cross platform caching solution

Getting ccache from my fork’s binary releases is as easy as:

- name: Download ccache
    id: ccache
    shell: cmake -P {0}
    run: |
      set(ccache_url "https://github.com/cristianadam/ccache/releases/download/v$ENV{CCACHE_VERSION}/${ { runner.os } }.tar.xz")
      file(DOWNLOAD "${ccache_url}" ./ccache.tar.xz SHOW_PROGRESS)
      execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./ccache.tar.xz)

Visual C++ support

I used Jean-Dominique Gascuel’s work from ccache’s PR 162. He tried to build ccache with Visual C++, add support for it in ccache. His pull request had 161 commits, and in the end got closed pensive

I just needed the last part, having support for Visual C++. I am fine with a MinGW build of ccache.

At the moment I have only tested CMake with Ninja generator in Release mode, which is exactly what I need for GitHub actions.

Debug mode is not supported since ccache should cache also the pdb files. Precompiled headers are not supported since ccache should know about them and store the pch files.

HelloWorld project

I have updated my C++ HelloWorld GitHub Actions enabled project to use ccache. The yaml file can be also downloaded from here.

Comments