On this page

Support for Build Automation Systems

Make and its variants

Coco can also generate code coverage information for a project without the need to modify it. The principle is to prepend to the PATH variable the path of the CoverageScanner compiler wrappers and to set the instrumentation parameters with the COVERAGESCANNER_ARGS environment variable. To activate the instrumentation, --cs-on must be present in COVERAGESCANNER_ARGS. If this is not the case, CoverageScanner is completely deactivated.

Note: The variable COVERAGESCANNER_ARGS should only be set locally, e.g. in a script or on the command line. If it is set globally, it will influence every build.

GNU Make

On a UNIX® system, proceed as follows to instrument a project which can be generated using GNU Make:

export PATH=/opt/SquishCoco/wrapper/bin:$PATH
export COVERAGESCANNER_ARGS=--cs-on
make clean
make

For macOS, replace the first line with:

export PATH=/Applications/SquishCoco/wrapper:$PATH

Microsoft NMake

To instrument a project that is generated with NMake:

set PATH=%SQUISHCOCO%\visualstudio;%PATH%
set COVERAGESCANNER_ARGS=--cs-on
nmake clean
nmake

Microsoft MSBuild

To instrument a project that is generated with MSBuild:

set PATH=%SQUISHCOCO%\visualstudio;%PATH%
set COVERAGESCANNER_ARGS=--cs-on
msbuild /p:UseEnv=true myproject.sln /t:ReBuild

CMake

CMake is a platform independent build tool from Kitware.

Note: This section is about stand-alone builds with CMake. For CMake as build tool for Qt Creator, please follow the link.

Instrumentation with a preloaded script

The following method to set up a CMake project for code coverage should work in all simple cases. (For an exception with Microsoft Visual Studio see below.)

There is a generic file, squishcoco.cmake, and specific setting files (like squishcoco-gcc.cmake) that specifies the compiler. The settings file is then included when the project is configured with CMake, with a command line like

cmake <other options> -Csquishcoco-gcc.cmake

Then the project is configured for code coverage.

If the settings file for a compiler is missing, one can modify one of the existing files to create it.

The file squishcoco.cmake

Save the following file as "squishcoco.cmake" in the root directory of your source code. Change the first line so that it contains the options that you need. The list of options must be a quoted string with the options separated by spaces. If no options are present, coverage is enabled.

This file cannot be used on its own (except with Qt Creator) but must be included from a compiler settings file (see below).

set(coverage_flags "--cs-mcdc --cs-no-assignments") # Set your own options

foreach(var IN ITEMS CMAKE_C_COMPILER CMAKE_CXX_COMPILER)
    if(NOT DEFINED ${var})
        message(FATAL_ERROR "Variable ${var} must be defined.")
    endif()
endforeach()

set(CMAKE_C_FLAGS_INIT "${coverage_flags}"
    CACHE STRING "Coverage flags for the C compiler." FORCE)
set(CMAKE_CXX_FLAGS_INIT "${coverage_flags}"
    CACHE STRING "Coverage flags for the C++ compiler." FORCE)
set(CMAKE_EXE_LINKER_FLAGS_INIT "${coverage_flags}"
    CACHE STRING "Coverage flags for the linker." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_INIT "${coverage_flags}"
    CACHE STRING "Coverage flags to link shared libraries." FORCE)
set(CMAKE_STATIC_LINKER_FLAGS_INIT "${coverage_flags}"
    CACHE STRING "Coverage flags to link static libraries." FORCE)

if (DEFINED ENV{SQUISHCOCO})
    set(cocopath $ENV{SQUISHCOCO})
else()
    find_file(cocopath SquishCoco
      PATHS "$ENV{HOME}" /opt/ "/Applications"
      REQUIRED
      NO_DEFAULT_PATH
    )
endif()

if(CMAKE_HOST_APPLE)
    set(wrapperdir "${cocopath}/")
elseif(CMAKE_HOST_UNIX)
    set(wrapperdir "${cocopath}/bin/")
elseif(MINGW)
    set(wrapperdir "${cocopath}\\bin\\")
else()
    set(wrapperdir "${cocopath}\\" )
endif()

get_filename_component(c_compiler ${CMAKE_C_COMPILER} NAME)
find_program(code_coverage_c_compiler cs${c_compiler}
    PATHS ${wrapperdir}
    REQUIRED NO_DEFAULT_PATH)
set(CMAKE_C_COMPILER "${code_coverage_c_compiler}"
    CACHE FILEPATH "CoverageScanner wrapper for C compiler" FORCE)

get_filename_component(cxx_compiler ${CMAKE_CXX_COMPILER} NAME)
find_program(code_coverage_cxx_compiler cs${cxx_compiler}
    PATHS ${wrapperdir}
    REQUIRED NO_DEFAULT_PATH)
set(CMAKE_CXX_COMPILER "${code_coverage_cxx_compiler}"
    CACHE FILEPATH "CoverageScanner wrapper for C++ compiler" FORCE)

if(DEFINED CMAKE_LINKER)
    get_filename_component(linker_prog ${CMAKE_LINKER} NAME)
    find_program(code_coverage_linker cs${linker_prog}
        PATHS ${wrapperdir}
        REQUIRED NO_DEFAULT_PATH)
    set(CMAKE_LINKER "${code_coverage_linker}"
        CACHE FILEPATH "CoverageScanner wrapper for linker" FORCE)
elseif(${c_compiler} STREQUAL "cl.exe") # special case for Visual Studio
    find_program(code_coverage_linker "cslink.exe"
        PATHS ${wrapperdir}
        REQUIRED NO_DEFAULT_PATH)
    set(CMAKE_LINKER "${code_coverage_linker}"
        CACHE FILEPATH "CoverageScanner wrapper for linker" FORCE)
endif()

if(DEFINED CMAKE_AR)
    get_filename_component(ar_prog ${CMAKE_AR} NAME)
    find_program(code_coverage_ar cs${ar_prog}
        PATHS ${wrapperdir}
        REQUIRED NO_DEFAULT_PATH)
    set(CMAKE_AR "${code_coverage_ar}"
        CACHE FILEPATH "CoverageScanner wrapper for ar" FORCE)
endif()

mark_as_advanced(
  cocopath
  code_coverage_c_compiler code_coverage_cxx_compiler code_coverage_linker code_coverage_ar
)

The file squishcoco-gcc.cmake

This file is needed to instrument a compilation with GCC. It must be in the same directory as squishcoco.cmake.

set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)
set(CMAKE_AR ar)
set(CMAKE_LINKER gcc)

include(squishcoco.cmake)

The file squishcoco-clang.cmake

This file is needed to instrument a compilation with Clang. It must be in the same directory as squishcoco.cmake.

set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_AR ar)
set(CMAKE_LINKER clang)

include(squishcoco.cmake)

The file squishcoco-visualstudio.cmake

Note: This approach does not work if you use the Visual Studio generators of CMake (which are the default under Windows). For them, the MSBuild instrumentation method must be used.
With the generators "NMake Makefiles", "NMake Makefiles JOM" and "Ninja", the instrumentation method works.

This file is needed to instrument a compilation with Microsoft Visual C/C++. It must be in the same directory as squishcoco.cmake.

set(CMAKE_C_COMPILER cl)
set(CMAKE_CXX_COMPILER cl)
set(CMAKE_AR lib)
set(CMAKE_LINKER link)
include(squishcoco.cmake)

Creating your own settings file

With these examples, it should be easy to customize the settings file for other compilers. It is only necessary to set the names of the programs used, not their full paths.

Instrumentation with a toolchain file

Note: This is an older method that only works when there is no toolchain file already in use. It is left in the documentation for the cases in which it is still needed.

When Coco is used with CMake, the changes are partially dependent on the toolchain that is used for compilation. We will now first describe the addition of a new build type, which is independent from the toolchain, and then the additional changes for Microsoft® Visual Studio® and GNU GCC.

Adding new build type for instrumented compilation

The first step is independent of the toolchain that is used. Its purpose is to declare the CMake variables that are used to specify the instrumented compilation. In CMake this is done by declaring a build type, which we will here call COVERAGE.

To do this, add to the to CMakeLists.txt file the following lines. The variable COVERAGE_FLAGS in the first line specifies the CoverageScanner command line options. Change its value to fit your needs. Only --cs-on must always be present.

SET(COVERAGE_FLAGS "--cs-on --cs-count")
SET(CMAKE_CXX_FLAGS_COVERAGE
    "${CMAKE_CXX_FLAGS_RELEASE} ${COVERAGE_FLAGS}" CACHE STRING
    "Flags used by the C++ compiler during coverage builds."
    FORCE )
SET(CMAKE_C_FLAGS_COVERAGE
    "${CMAKE_C_FLAGS_RELEASE} ${COVERAGE_FLAGS}" CACHE STRING
    "Flags used by the C compiler during coverage builds."
    FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
    "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${COVERAGE_FLAGS}" CACHE STRING
    "Flags used for linking binaries during coverage builds."
    FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
    "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${COVERAGE_FLAGS}" CACHE STRING
    "Flags used by the shared libraries linker during coverage builds."
    FORCE )
SET( CMAKE_STATIC_LINKER_FLAGS_COVERAGE
    "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} ${COVERAGE_FLAGS}" CACHE STRING
    "Flags used by the static libraries linker during coverage builds."
    FORCE )
MARK_AS_ADVANCED(
    CMAKE_CXX_FLAGS_COVERAGE
    CMAKE_C_FLAGS_COVERAGE
    CMAKE_EXE_LINKER_FLAGS_COVERAGE
    CMAKE_SHARED_LINKER_FLAGS_COVERAGE
    CMAKE_STATIC_LINKER_FLAGS_COVERAGE
    COMPILE_DEFINITIONS_COVERAGE
)

These commands take the compiler and linker flags of the Release build type and add to them the coverage flags. If you want to use instead the flags of another build type, replace the suffix _RELEASE in this code with the name of another build type, such as _DEBUG.

Microsoft Visual Studio

In Visual Studio, we need to make the new Coverage build type visible to the IDE. To do this, we add an item to the configurations list in in CMakeSettings.json that is based on your Release configuration.

{
"configurations": [
     "_comment" : "other configurations here... ",
      {
        "name": "CodeCoverage-x64-Release",
        "generator": "Ninja",
        "configurationType": "Release",
        "buildRoot": "${projectDir}\\out\\build\\${name}",
        "installRoot": "${projectDir}\\out\\install\\${name}",
        "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=cl.cmake -DCMAKE_BUILD_TYPE=COVERAGE",
        "buildCommandArgs": "",
        "ctestCommandArgs": "",
        "inheritEnvironments": [ "msvc_x64_x64" ],
        "variables": []
      }
   ]
}

The important detail here is the cmakeCommandArgs which tells cmake to use the cl.cmake toolchain file, and to use the COVERAGE build type. The other arguments/variables can be copied or customized from another working configuration.

Compilation with Microsoft NMake

In a project that is compiled with NMake:

  1. Create a toolchain definition file cl.cmake which replaces the compiler and linker with their CoverageScanner wrappers.

    For example:

    # this one is important
    SET(CMAKE_SYSTEM_NAME Windows)
    
    # specify the cross compiler
    FILE(TO_CMAKE_PATH "$ENV{SQUISHCOCO}/visualstudio" SQUISHCOCO)
    SET(CMAKE_C_COMPILER ${SQUISHCOCO}/cl.exe
        CACHE FILEPATH "CoverageScanner wrapper" FORCE)
    SET(CMAKE_CXX_COMPILER ${SQUISHCOCO}/cl.exe
        CACHE FILEPATH "CoverageScanner wrapper" FORCE)
    SET(CMAKE_LINKER ${SQUISHCOCO}/link.exe
        CACHE FILEPATH "CoverageScanner wrapper" FORCE)
  2. Create a Makefile project. Set the toolchain to the CoverageScanner wrapper and the build mode to COVERAGE.

    For example:

    cmake.exe -DCMAKE_TOOLCHAIN_FILE=cl.cmake -DCMAKE_BUILD_TYPE=COVERAGE \
              -G "NMake Makefiles" <i>path of cmake project</i>
  3. Build the project with nmake.

Compilation with GNU GCC

The following must be done in a project that is compiled with gcc:

  1. Create a toolchain definition file gcc.cmake which replaces the compiler and linker with their CoverageScanner wrappers.

    For example:

    find_program(CODE_COVERAGE_GCC gcc
        PATHS /opt/SquishCoco/wrapper/bin "$ENV{HOME}/SquishCoco/wrapper/bin"
        NO_DEFAULT_PATH )
    find_program(CODE_COVERAGE_GXX g++
        PATHS /opt/SquishCoco/wrapper/bin "$ENV{HOME}/SquishCoco/wrapper/bin"
        NO_DEFAULT_PATH )
    find_program(CODE_COVERAGE_AR ar
        PATHS /opt/SquishCoco/wrapper/bin "$ENV{HOME}/SquishCoco/wrapper/bin"
        NO_DEFAULT_PATH )
    
    # specify the cross compiler
    SET(CMAKE_C_COMPILER "${CODE_COVERAGE_GCC}"
        CACHE FILEPATH "CoverageScanner wrapper" FORCE)
    SET(CMAKE_CXX_COMPILER "${CODE_COVERAGE_GXX}"
        CACHE FILEPATH "CoverageScanner wrapper" FORCE)
    SET(CMAKE_LINKER "${CODE_COVERAGE_GXX}"
        CACHE FILEPATH "CoverageScanner wrapper" FORCE)
    SET(CMAKE_AR "${CODE_COVERAGE_AR}"
        CACHE FILEPATH "CoverageScanner wrapper" FORCE)
  2. Create a Makefile project. Set the toolchain to the CoverageScanner wrapper and the build mode to COVERAGE.

    For example:

    cmake -DCMAKE_TOOLCHAIN_FILE=gcc.cmake -DCMAKE_BUILD_TYPE=COVERAGE \
          -G "Unix Makefiles" <i>path of cmake project</i>
  3. Build the project with make.

Qt framework

Note: Newer Qt versions also support CMake as its build system. The instructions in the CMake chapter apply to Qt projects too.

qmake

qmake is a tool that is used by Qt to generate a makefile in a platform-independent way, using as specification a so-called project file. It is also used by Qt Creator.

By default, qmake chooses the programs that are used for compilation. We can use the Coco wrappers by setting some of qmake's variables to new values. This can be done by putting some declarations into the qmake project files. Since one needs to build the project with and without coverage, the new definitions must be put into a scope. A scope is a region in a qmake project file that can be activated on demand.

The following listing shows a template for such a scope, named CoverageScanner, which should be sufficient for most projects.

CodeCoverage {
    COVERAGE_OPTIONS =

    QMAKE_CFLAGS   += $$COVERAGE_OPTIONS
    QMAKE_CXXFLAGS += $$COVERAGE_OPTIONS
    QMAKE_LFLAGS   += $$COVERAGE_OPTIONS

    defineReplace(toCoco) {
        cmd = $$1
        path = $$take_first(cmd)
        prog = $$basename(path)

        return(cs$$prog $$cmd)
    }

    QMAKE_AR = $$toCoco($$QMAKE_AR)
    QMAKE_CC = $$toCoco($$QMAKE_CC)
    QMAKE_CXX = $$toCoco($$QMAKE_CXX)
    QMAKE_LIB = $$toCoco($$QMAKE_LIB)
    QMAKE_LINK = $$toCoco($$QMAKE_LINK)
    QMAKE_LINK_SHLIB_CMD = $$toCoco($$QMAKE_LINK_SHLIB_CMD)
}

Here we also set the variables QMAKE_LINK_SHLIB and QMAKE_AR, which contain the names of the command to link shared libraries and that to generate archives. Furthermore, you can use COVERAGE_OPTIONS to set coveragescanner command line options (see List of options) to customize the project. An empty value for COVERAGE_OPTIONS will also work and results in a default instrumentation.

In a small project, the CodeCoverage scope may then be copied to all profile files of the project, i.e. those that end in .pro. In fact, it is enough to insert them only into those files that actually compile code and not those that only include other files. If the project is larger, it has very often a file with common settings that is included by all profiles: This is then the most convenient place to insert the CodeCoverage scope only once.

The new coverage scope is by default inactive. To enable code coverage in a project you want to built, just add the name of the scope to the CONFIG variable when configuring it with qmake:

$ qmake CONFIG+=CodeCoverage

moc

The Meta-Object Compiler (moc) generates some methods for each class derived from QObject. For example, the translation function tr, the source code for all signals, the cast operator qt_cast, …In order to instrument the code using the Qt Framework and not moc-generated code, CoverageScanner provides the command line options --cs-qt3 for Qt3 and --cs-qt4 for Qt4 to Qt6. They are enabled by default.

In this case:

  • Q_OBJECT macro is no longer instrumented.
  • All signals are instrumented in order to track their emission.
  • The glue code necessary for the signal/slot mechanism is not instrumented.
  • The code of Q_FOREACH macro is not instrumented in Qt4.

qbs

To use CoverageScanner with the Qt Build Suite (qbs), set it up as a toolchain. You can then use the toolchain for all qbs projects.

To set up CoverageScanner as a toolchain, issue the following command:

qbs setup-toolchains --type gcc /opt/SquishCoco/bin/csgcc csgcc

For Unix-based operating systems, some additional configuration steps are necessary:

qbs config profiles.csgcc.cpp.archiverPath /opt/SquishCoco/bin/csar
qbs config profiles.csgcc.cpp.linkerName csg++
qbs config profiles.csgcc.cpp.nmPath /opt/SquishCoco/bin/csgcc-nm

The csgcc toolchain can then also be used as base profile for Qt projects:

qbs setup-qt /opt/Qt/bin/qmake qt-csgcc
qbs config profiles.qt-csgcc.baseProfile csgcc

SCons

To use Coco with SCons:

  1. Prepend the path of CoverageScanner's wrapper (csgcc, cscl, and son on) to the PATH environment variable. The PATH environment variable should be set to be able to execute CoverageScanner wrappers.
  2. Set CC, AR and LINK variables to the corresponding CoverageScanner wrapper. For example: when using Microsoft Visual Studio compiler, set CC to cscl.exe, LINK to cslink.exe, and AR to cslib.exe.

    Note: Do no use absolute file paths to the compiler wrapper since some versions of SCons do not properly handle spaces in file names.

  3. Add code coverage settings to exclude from the instrumentation for example files to the variables CCFLAGS, ARFLAGS and LINKFLAGS.

Here is a code snippet which can be used for Visual Studio command line tools:

import os
from os.path import pathsep

env = Environment()

# Add the path of Squish Coco compiler wrapper
env[ 'ENV' ][ 'PATH' ] = os.environ[ 'SQUISHCOCO' ] + pathsep + env[ 'ENV' ][ 'PATH' ]
# TEMP variable need to be defined
env[ 'ENV' ][ 'TEMP' ] = os.environ[ 'TEMP' ]
env[ 'ENV' ][ 'INCLUDE' ] = os.environ[ 'INCLUDE' ]

# Set the compiler to Squish Coco wrappers
env[ 'CC' ]   = 'cs' + env[ 'CC' ] ;
env[ 'AR' ]   = 'cs' + env[ 'AR' ] ;
env[ 'LINK' ] = 'cs' + env[ 'LINK' ] ;

# Code coverage settings
coverageflags = [ '--cs-count' ]
env[ 'CCFLAGS' ]   = env[ 'CCFLAGS' ] + coverageflags ;
env[ 'ARFLAGS' ]   = env[ 'ARFLAGS' ] + coverageflags ;
env[ 'LINKFLAGS' ] = env[ 'LINKFLAGS' ] + coverageflags ;

To correctly set the build environment, you must start the build from the Visual Studio's developer prompt (available through the start menu) or the Coco's console provided by Build Environment Selection.

Bazel

Bazel chooses the compiler through a C++ toolchain, and it keeps track of every file that a build action creates. Putting the CoverageScanner wrappers in the PATH is therefore not enough: Bazel must be told to use them, and the .csmes instrumentation database that each wrapper writes must be handled as a build output.

Integrating Coco with a Bazel build

The integration consists of the following parts:

  • A C++ toolchain whose compile, link and archive tools are the CoverageScanner wrappers.
  • The CoverageScanner options, which the toolchain passes to the wrappers in the COVERAGESCANNER_ARGS environment variable.
  • The forwarding of PATH and HOME to the build actions so that the wrappers and the compiler can be found, and the license configuration in the home directory can be read.
  • A way to ensure that Bazel correctly handles the .csmes files
  • A test listener that records the coverage of each test separately, and a script that merges the results and creates the report.

The parser-bazel sample

The parser-bazel sample shows these parts in a working project. It can be found at https://git.qt.io/coco/samples/parser-bazel. In future versions, this sample will be found in the Coco installation directory.

It is a small arithmetic expression parser, based on the parser sample introduced in Instrumentation of a simple project but uses Bazel for building. It also contains additional structure, with a few libraries, two command-line front ends and a test suite that uses GoogleTest. It has three different versions, each a complete Bazel workspace:

  • parser_bazel_v1: the plain Bazel project, without Coco.
  • parser_bazel_v2: Coco added with a local toolchain.
  • parser_bazel_v3: Coco added with a toolchain whose outputs bundle the .csmes files, so that builds run sandboxed and can use Bazel's disk cache.

To navigate the workspaces, you can start at the following locations:

  • The README.md file in each workspace for general information about the sample.
  • The COVERAGE.md files in parser_bazel_v2 and parser_bazel_v3 for coverage setup and usage.
  • The bazel/coco directory in parser_bazel_v2 and parser_bazel_v3 for the Coco integration details.

The integration is written for Linux with GCC and was tested with Bazel 9.3.0. However, the approaches are applicable to other compilers such as Clang or Microsoft® Visual C/C++. To adapt it, you would need to implement the toolchain definition for the compiler you want to use, as well as other necessary adjustments in the bazel/coco files for the build and test environment.

Building and testing

Building without Coco

To build the version without Coco, change to parser_bazel_v1 and run:

bazel build //...

The same command also works for parser_bazel_v2 and parser_bazel_v3 to build them without using Coco.

Building with Coco

Change to parser_bazel_v2 or parser_bazel_v3 and proceed as follows:

  1. Check that Coco and its license work for your user. Replace /opt/SquishCoco with your Coco installation directory.
    /opt/SquishCoco/bin/cocolic --check
  2. Make the bin directory of Coco available in the PATH. If you cannot change the PATH, create the file user.bazelrc in the workspace directory containing the following:
    build:coco --repo_env=SQUISHCOCO=/opt/SquishCoco
  3. Build and test with Coco:
    bazel test //... --config=coco

    You should see six of the seven tests pass. The test parser_test fails in ParserTest.testInvalidNumber, which was added intentionally to demonstrate a failing test case.

  4. Optionally, create the coverage report of the tests. Run the commands from the workspace directory:
    bazel test --config=coco --nocache_test_results //...
    sh bazel/coco/scripts/report.sh report \
        libs/builtins/builtins_test libs/cutil/cutil_test libs/engine/engine_test \
        libs/format/format_test libs/parser/parser_test libs/support/support_test \
        tests/integration/integration_test

    The result is written to the directory report/, and the HTML report is in report/merged.html.

Coco integration approaches

parser_bazel_v2 and parser_bazel_v3 differ in how the .csmes files are handled by Bazel.

Local toolchain

parser_bazel_v2 adds a local toolchain that uses the CoverageScanner wrappers instead of the default C++ toolchain. See parser_bazel_v2/bazel/coco/coco_toolchain.bzl for the implementation details.

By default, CoverageScanner writes the .csmes files next to the instrumented binaries where Bazel does not know about them. Bazel tracks only the declared outputs of the actions, so the danger is that the .csmes files may be ignored and lost. The actions must therefore run unsandboxed and without the caches to ensure that the .csmes files are preserved. This is implemented by the following lines in the .bazelrc of parser_bazel_v2:

build:coco --spawn_strategy=local
build:coco --disk_cache=
build:coco --noremote_accept_cached
build:coco --noremote_upload_local_results

The first line runs the actions unsandboxed. The other three turn off the caches.

This approach fits a development machine, or a single-node CI machine where unsandboxed, uncached builds are acceptable. It is the quickest way to obtain coverage of the whole tree, and a good first integration. It does not fit remote execution or caching, sandboxed or hermetic CI, or shared CI where the results must match across machines.

Toolchain with bundled csmes files

In parser_bazel_v3, a workaround is used to ensure that the .csmes files are preserved even when Bazel runs the actions sandboxed and uses the disk cache. Each Coco output is packed together with its .csmes file into a single file that Bazel expects as the output: foo.o, libfoo.a or the executable. Thus, no extra files such as the .csmes file need to be declared as outputs.

In parser_bazel_v3, the bundling is handled by helper executables such as compiler_wrapper and csmes_extract that are compiled before the actual instrumented build. They use custom bundling formats to combine the output files and their corresponding .csmes files into a single file:

foo.o (bundle) = "CSMESPK1"
                 + 4-byte count (2)
                 + [ 2-byte name length, "foo.o",       8-byte size, foo.o contents ]
                 + [ 2-byte name length, "foo.o.csmes", 8-byte size, foo.o.csmes contents ]

app (executable) = app contents
                   + app.csmes contents
                   + "CSMESBUNDLE" + 20-digit length of app.csmes + "\n"

However, other formats or tools can also be used to bundle the .csmes files. For example, one could use a standard archive format such as tar or zip to bundle the .csmes files with their corresponding outputs, or use a shell script or Python script instead of a helper executable.

In this setup, the coco configuration in .bazelrc lacks the four lines in the local toolchain section, so the default sandbox and the disk cache stay on.

This approach fits a development machine, or a CI machine that reuses its own cache within one checkout. However, because the Coco instrumentation depends on absolute paths, it does not fit remote caching across machines or builds that expect cache hits from another checkout.

Coco v7.5.2 ©2026 The Qt Company Ltd.
Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.