CTest, CMake и MinGW: исполняемые файлы собираются, но не запускаются, потому что не найдена свежая DLL

Верхний уровень CMakeLists.txt содержит:

include(CTest)
add_subdirectory(lib)
add_subdirectory(demo)
add_subdirectory(test)

lib/CMakeLists.txt по сути:

add_library(MyLib <sources>)

demo/CMakeLists.txt по сути:

add_executable(Demo demo.c)
target_link_libraries(Demo MyLib)

test/CMakeLists.txt просто:

add_test(NAME Demo COMMAND Demo)

Из gitlab-runner выполняем:

cmake -G "Ninja" -DCMAKE_INSTALL_PREFIX=C:\opt\x64 -B. ..
cmake --build
ctest --output-on-failure

Первые два шага выполнены успешно; третий не работает с:

Start 1: Demo
1/1 Test #1: Demo .......................Exit code 0xc0000135
***Exception:   0.03 sec

Если я попробую еще раз:

cmake --install
ctest

тогда тест проходит успешно. Итак, единственная проблема в том, что build/lib/mylib.dll не обнаруживается при запуске ctest. В то время как C:\opt\x64\lib находится в PATH, и поэтому DLL находится после cmake --install. Однако это не то, что нам нужно: ctest всегда будет использовать свежую DLL из текущей сборки, а не установленную версию.

Под линуксом все работает корректно. Почему нет для Windows и MinGW? Это ошибка в CMake? Как мы можем обойти это, чтобы ctest правильно выполнялся на всех платформах?


person Joachim W    schedule 17.12.2019    source источник
comment
Отвечает ли это на ваш вопрос? Как скопировать DLL файлы в ту же папку, что и исполняемый файл с помощью CMake?   -  person fdk1342    schedule 17.12.2019
comment
Я отредактировал свой вопрос, чтобы прояснить, что речь идет не только об обходном пути, но и о понимании почему чистого стандартного кода CMake не работает.   -  person Joachim W    schedule 17.12.2019
comment
Он работает правильно. Ваша среда выполнения неверна, и вам нужно это исправить. Это ничем не отличается от установки RPATH в Linux, если вы создали уже установленную разделяемую библиотеку и вам нужно использовать правильную.   -  person fdk1342    schedule 17.12.2019


Ответы (1)


Кажется, ваша проблема заключается в том, что процедура поиска Windows DLL не может найти mylib.dll, когда ваш Demo исполняемый файл запускается ctest. Порядок поиска Windows DLL указан здесь:

  1. Каталог, из которого загружено приложение.
  2. Системный каталог. Используйте функцию GetSystemDirectory, чтобы получить путь к этому каталогу.
  3. 16-битный системный каталог. Нет функции, которая получает путь к этому каталогу, но он ищется.
  4. Каталог Windows. Используйте функцию GetWindowsDirectory, чтобы получить путь к этому каталогу.
  5. Текущий каталог.
  6. Каталоги, перечисленные в переменной среды PATH. Обратите внимание, что сюда не входит путь для каждого приложения, указанный в разделе реестра App Paths. Ключ App Paths не используется при вычислении пути поиска DLL.

Таким образом, вы можете изменить переменную среды PATH, чтобы также включать расположение новой библиотеки DLL из текущей сборки.

Лучшим и менее подверженным ошибкам решением может быть размещение библиотеки DLL в том же каталоге, что и исполняемый файл Demo. Вы можете заставить CMake использовать один и тот же двоичный каталог как для DLL, так и для исполняемого файла, изменив файл CMake верхнего уровня:

include(CTest)
add_subdirectory(lib ${CMAKE_BINARY_DIR}/demo)
add_subdirectory(demo ${CMAKE_BINARY_DIR}/demo)
add_subdirectory(test)

В качестве альтернативы, в качестве менее локализованного подхода вы можете поместить DLL в тот же каталог, что и исполняемый файл, установив _ 12_:

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

Еще одна альтернатива:

add_test(NAME Demo COMMAND Demo WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
person squareskittles    schedule 17.12.2019
comment
Я предпочитаю использовать CMAKE_RUNTIME_OUTPUT_DIRECTORY , чтобы поместить DLL и exe в одну и ту же папку, вместо того, чтобы указывать расположение двоичной папки с помощью add_subdirectory(). - person fdk1342; 17.12.2019
comment
@ fdk1342 Да, это тоже допустимый подход, хотя он кажется менее локализованным, поскольку я считаю, что он применим ко всем целям. Однако вы можете установить свойство для конкретной цели, используя RUNTIME_OUTPUT_DIRECTORY < / а>. - person squareskittles; 17.12.2019
comment
Библиотека - это наш продукт; демонстрационные программы и тесты являются вспомогательными. Поэтому мне не хотелось бы переносить библиотеку из lib в demo. Есть ли в CMake идиоматический способ сообщить демонстрационным программам, где найти DLL? - person Joachim W; 17.12.2019
comment
Если вы не возражаете, я отредактирую ваш ответ, добавив третий подход: вызовите add_test с WORKING_DIRECTORY, указывающим на lib. - person Joachim W; 17.12.2019
comment
@JoachimW Да, если это лучше всего решит вашу проблему, не стесняйтесь вносить изменения или размещать свой собственный ответ! - person squareskittles; 17.12.2019