The top-level CMakeLists.txt
contains:
include(CTest)
add_subdirectory(lib)
add_subdirectory(demo)
add_subdirectory(test)
lib/CMakeLists.txt
is essentially:
add_library(MyLib <sources>)
demo/CMakeLists.txt
is essentially:
add_executable(Demo demo.c)
target_link_libraries(Demo MyLib)
test/CMakeLists.txt
is just:
add_test(NAME Demo COMMAND Demo)
From a gitlab-runner, we execute:
cmake -G "Ninja" -DCMAKE_INSTALL_PREFIX=C:\opt\x64 -B. ..
cmake --build
ctest --output-on-failure
The first two steps succeed; the third one fails with:
Start 1: Demo
1/1 Test #1: Demo .......................Exit code 0xc0000135
***Exception: 0.03 sec
If I retry:
cmake --install
ctest
then the test succeeds. So the sole problem is that build/lib/mylib.dll
is not found when running ctest
. Whereas C:\opt\x64\lib
is in PATH
, and therefore the DLL is found after cmake --install
. Which, however, is not what we want: ctest
shall always use the fresh DLL from the current build, not the installed version.
Under Linux, everything works correctly. Why doesn't it for Windows and MinGW? Is this a bug in CMake? How can we work around this so that ctest
executes correctly on all platforms?
Your issue seems to be the Windows DLL search procedure failing to find mylib.dll
when your Demo
executable is run by ctest
. The Windows DLL search order is specified here:
- The directory from which the application loaded.
- The system directory. Use the
GetSystemDirectory
function to get the path of this directory.- The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
- The Windows directory. Use the
GetWindowsDirectory
function to get the path of this directory.- The current directory.
- The directories that are listed in the
PATH
environment variable. Note that this does not include the per-application path specified by theApp Paths
registry key. TheApp Paths
key is not used when computing the DLL search path.
So, you could modify your PATH
environment variable to also include the location of the fresh DLL from the current build.
A better, less error-prone solution might be to place the DLL in the same directory as the Demo
executable. You can force CMake to use the same binary directory for both the DLL and executable by modifying your top-level CMake file:
include(CTest)
add_subdirectory(lib ${CMAKE_BINARY_DIR}/demo)
add_subdirectory(demo ${CMAKE_BINARY_DIR}/demo)
add_subdirectory(test)
Alternatively, as a less localized approach, you can place the DLL in the same directory as the executable by setting CMAKE_RUNTIME_OUTPUT_DIRECTORY
:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
Yet another alternative:
add_test(NAME Demo COMMAND Demo WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib)