NB: The files referenced here are all given below the horizontal ruler a bit down.
This is the MWE derived from a project where I ran into this. CMake version used is 3.12.2 on Ubuntu 16.04 (16.04.6 to be precise).
The goal is to create a CMakeLists.txt
which can be re-targeted to build a Windows DLL using the MinGW-w64 toolchain (apt-get install mingw-w64
on Ubuntu/Debian).
When using no explicit toolchain file (i.e. without -DCMAKE_TOOLCHAIN_FILE=...
), all works as expected and the lib${PRJNAME}.so
gets installed as desired. However, once I use the toolchain file given below, I only get the resulting import lib ${PRJNAME}Lib.dll.a
but not the corresponding .dll
file installed.
If I invoke the Bash script as follows:
./build.sh 2>&1 |grep '^-- Install'
the output is:
-- 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
As you can see the native build installs the actual shared library, but the one targeting Windows only installs the import lib, not the DLL. What I'd expect to see is something like this:
-- 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
What is it I am doing wrong here? It would seem that the install()
function is invoked properly:
install(
TARGETS ${PRJNAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT library
)
Clearly ARCHIVE DESTINATION
takes effect as that's where the import lib ends up. But why is the built .dll
completely ignored here?
Side-note: I am aware of GNUInstallDirs
, but that fell totally apart once I started cross-compiling for Windows. So I am setting the desired paths "manually" before invoking install()
.
The script will first wipe the folders build-native
and build-windows
, if present, and then create those again. Then it will invoke cmake
from these folders respectively, targeting using the system (native) toolchain and the MinGW-w64 toolchain respectively. Last but not least it will invoke the installation from these folders respectively.
So if you place this into an empty folder along with the other files this should not meddle with your data elsewhere in any way.
#/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
)
According to the install
command documentation the DLL file is considered a runtime object. I tested the example on Ubuntu 14.04.