Примечание: Файлы, на которые имеются ссылки здесь, приведены ниже горизонтальной линейки немного вниз.
Это MWE, полученный из проекта, в котором я столкнулся с этим. Используемая версия CMake — 3.12.2 в Ubuntu 16.04 (точнее, 16.04.6).
Цель состоит в том, чтобы создать CMakeLists.txt
, который можно перенаправить на сборку Windows DLL с помощью цепочки инструментов MinGW-w64 (apt-get install mingw-w64
в Ubuntu/Debian).
При использовании без явного файла цепочки инструментов (т. е. без -DCMAKE_TOOLCHAIN_FILE=...
) все работает, как и ожидалось, и lib${PRJNAME}.so
устанавливается по желанию. Однако, как только я использую файл цепочки инструментов, приведенный ниже, я получаю только результирующую библиотеку импорта ${PRJNAME}Lib.dll.a
, но не соответствующий установленный файл .dll
.
Если я вызову сценарий Bash следующим образом:
./build.sh 2>&1 |grep '^-- Install'
вывод:
-- Install configuration: ""
-- Installing: /home/user/cmake-test/build-native/install-target/lib/libtest.so
-- Install configuration: ""
-- Installing: /home/user/cmake-test/build-windows/install-target/lib/test.dll.a
Как вы можете видеть, собственная сборка устанавливает общую библиотеку действительный, но сборка, ориентированная на Windows, устанавливает только библиотеку импорта, нет — DLL. Я ожидал увидеть что-то вроде этого:
-- Install configuration: ""
-- Installing: /home/user/cmake-test/build-native/install-target/lib/libtest.so
-- Install configuration: ""
-- Installing: /home/user/cmake-test/build-windows/install-target/lib/test.dll
-- Installing: /home/user/cmake-test/build-windows/install-target/lib/test.dll.a
Что я здесь делаю неправильно? Казалось бы, функция install()
вызывается правильно:
install(
TARGETS ${PRJNAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT library
)
Очевидно, что ARCHIVE DESTINATION
вступает в силу, так как именно здесь заканчивается импортная библиотека. Но почему встроенный .dll
здесь полностью игнорируется?
Примечание: я знаю о GNUInstallDirs
, но он полностью развалился, как только я начал кросс-компиляцию для Windows. Поэтому я устанавливаю нужные пути «вручную» перед вызовом install()
.
Скрипт сначала сотрет папки build-native
и build-windows
, если они есть, а затем создаст их снова. Затем он будет вызывать cmake
из этих папок соответственно, ориентируясь на системный (собственный) набор инструментов и набор инструментов MinGW-w64 соответственно. И последнее, но не менее важное: он вызовет установку из этих папок соответственно.
Поэтому, если вы поместите это в пустую папку вместе с другими файлами, это никоим образом не должно мешать вашим данным в другом месте.
#/usr/bin/env bash
for i in native windows; do
D=build-$i
test -d $D && rm -rf $D
mkdir $D
[[ "$i" == "windows" ]] && TCFILE=mingw64-64bit.cmake
( set -x; cd $D && cmake .. -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=. ${TCFILE+-DCMAKE_TOOLCHAIN_FILE=$TCFILE} -DCMAKE_VERBOSE_MAKEFILE=ON )
( set -x; cd $D && cmake --build . --target install )
done
#ifdef _WIN32
# if defined(test_EXPORTS)
# define TEST_API __declspec(dllexport)
# else
# define TEST_API __declspec(dllimport)
# endif
#else
# define TEST_API
#endif
TEST_API void SomeFunction()
{
}
cmake_minimum_required(VERSION 3.12)
set(PRJNAME test)
set(TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/install-target")
project(${PRJNAME})
add_library(${PRJNAME} SHARED test.cpp)
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(DLL_PREFIX)
set(DLL_POSTFIX Lib)
else()
set(DLL_PREFIX lib)
set(DLL_POSTFIX)
endif ()
set_target_properties(
${PRJNAME}
PROPERTIES
PREFIX "${DLL_PREFIX}"
IMPORT_PREFIX "${DLL_PREFIX}"
DEBUG_POSTFIX "${DLL_POSTFIX}"
RELEASE_POSTFIX "${DLL_POSTFIX}"
CXX_STANDARD 11
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE 1
)
set(CMAKE_INSTALL_PREFIX ${TARGET_DIR})
set(CMAKE_INSTALL_LIBDIR lib)
set(CMAKE_INSTALL_INCLUDEDIR include)
install(
TARGETS ${PRJNAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT library
)
set(CMAKE_SYSTEM_NAME Windows)
set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc-posix)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++-posix)
set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
install(
TARGETS ${PRJNAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT library
)
В соответствии с документацией по команде install
файл DLL считается объектом времени выполнения. Я тестировал пример на Ubuntu 14.04.
@ 0xC0000022L Мое единственное предположение заключается в том, что в Windows легко разместить DLL в том же месте, что и исполняемый файл, чтобы при запуске исполняемого файла он легко находил DLL.
Спасибо, это действительно было проблемой. Совсем не очевидно, почему один будет считаться
RUNTIME
, а другойLIBRARY
... да ладно.