c++unit-testingcmakecross-compilingcatch2

Cross compiling from Linux to Windows using Catch2 Fails


I've a C++ project built using CMake v3.25.0. The project uses Catch2 v3.7.0 for unit testing. For development convenience I'm trying to cross-compile it from Linux to Windows. Following CMake's cross compilation guideline, I've the following windows.cmake file in my project:

# the name of the target operating system
set(CMAKE_SYSTEM_NAME Windows)

# which compilers to use for C and C++
set(CMAKE_C_COMPILER   x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)

# where is the target environment located
set(CMAKE_FIND_ROOT_PATH  /usr/x86_64-w64-mingw32)

# adjust the default behavior of the FIND_XXX() commands:
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

I build as follows:

cmake -DCMAKE_TOOLCHAIN_FILE=windows.cmake --preset="$PRESET" -S .
cmake --build --preset="$PRESET" --parallel "$(nproc)" --target install

This is the CMakeLists.txt file that builds the test binary:

Include(FetchContent)

FetchContent_Declare(
  Catch2
  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
  GIT_TAG        v3.7.0
)

FetchContent_MakeAvailable(Catch2)

list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)

add_executable(tests
  tests.cpp
  ${CMAKE_SOURCE_DIR}/src/filesystem_api.cpp
)
target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
target_include_directories(tests
    PRIVATE
        ${CMAKE_SOURCE_DIR}/include
)

include(CTest)
include(Catch)
catch_discover_tests(tests)

When the build begins, CMake correctly identifies cross-compiler:

Preset CMake variables:

  CMAKE_BUILD_TYPE="Release"
  CMAKE_CXX_FLAGS_RELEASE="-O3"
  CMAKE_C_FLAGS_RELEASE="-O3"
  CMAKE_INSTALL_PREFIX:PATH="/home/nishad/project/dir-stats/out/win-release/bin"

-- The CXX compiler identification is GNU 13.0.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/x86_64-w64-mingw32-g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Performing Test HAVE_FLAG__ffile_prefix_map__home_nishad_project_dir_stats_out_win_release__deps_catch2_src__
-- Performing Test HAVE_FLAG__ffile_prefix_map__home_nishad_project_dir_stats_out_win_release__deps_catch2_src__ - Success
-- Configuring done (10.0s)
-- Generating done (0.0s)

It then builds Catch2, builds main binary and test binary tests.exe. After building and linking tests.exe, it throws the following error:

[ 96%] Linking CXX static library libCatch2.a
[ 96%] Built target Catch2
[ 97%] Building CXX object _deps/catch2-build/src/CMakeFiles/Catch2WithMain.dir/catch2/internal/catch_main.cpp.obj
[ 97%] Linking CXX static library libCatch2Main.a
[ 97%] Built target Catch2WithMain
[ 99%] Building CXX object tests/CMakeFiles/tests.dir/tests.cpp.obj
[ 99%] Building CXX object tests/CMakeFiles/tests.dir/__/src/filesystem_api.cpp.obj
[100%] Linking CXX executable tests.exe
CMake Error at /home/nishad/project/dir-stats/out/win-release/_deps/catch2-src/extras/CatchAddTests.cmake:81 (message):
  Error running test executable
  '/home/nishad/project/dir-stats/out/win-release/tests/tests.exe':

    Result: 53
    Output:

Call Stack (most recent call first):
  /home/nishad/project/dir-stats/out/win-release/_deps/catch2-src/extras/CatchAddTests.cmake:193 (catch_discover_tests_impl)


gmake[2]: *** [tests/CMakeFiles/tests.dir/build.make:120: tests/tests.exe] Error 1
gmake[2]: *** Deleting file 'tests/tests.exe'
gmake[1]: *** [CMakeFiles/Makefile2:205: tests/CMakeFiles/tests.dir/all] Error 2
gmake: *** [Makefile:136: all] Error 2

It looks like it tried to run tests.exe on Linux host, which will fail obviously. Tracing back the error, it is initiated from here: https://github.com/catchorg/Catch2/blob/v3.7.0/extras/CatchAddTests.cmake#L74, which seems like part of the unit test build process by Catch2.

Is there any way to only build tests.exe but not run when cross-compiling?


Solution

  • catch_discover_tests needs to run a test executable for obtain a list of tests. By default, the executable is run just after it is built, at build stage. You may set parameter DISCOVERY_MODE to PRE_TEST for defer running the executable before running tests:

    catch_discover_tests(tests DISCOVERY_MODE PRE_TEST)
    

    Alternatively, you could set the variable CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE to PRE_TEST, so you won't need to modify every call to catch_discover_tests:

    set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE PRE_TEST)
    ...
    catch_discover_tests(tests)
    

    You could set the variable in your toolchain file, so your toolchain will work also with external projects, which code you don't want to change.

    All these aspects are described in the Catch documentation.