Copying build-dependent dlls to working directory with CMake - dll

So, I've searched for a while through internet and I still have to find a satisfactory solution to the following:
I have a CMake project on Windows (actually, it should work on Linux too, but I guess it'll be almost the same, since the real issue comes from having multiple configurations: Release, Debug and Release_Debug).
I'm using several 3rd-party libraries, among others (just to make an example): pthread, openssl and qt.
Qt and openssl have their configuration scripts bundled with CMake, for pthread I've created an imported target with the following:
set_target_properties(pthread PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_DIR}/pthread/include"
IMPORTED_IMPLIB_RELEASE "${EXTERNAL_DIR}/pthread/lib/pthreadVC3.lib"
IMPORTED_IMPLIB_DEBUG "${EXTERNAL_DIR}/pthread/lib/pthreadVC3d.lib"
IMPORTED_LOCATION_RELEASE "${EXTERNAL_DIR}/pthread/bin/pthreadVC3.dll"
IMPORTED_LOCATION_DEBUG "${EXTERNAL_DIR}/pthread/bin/pthreadVC3d.dll"
MAP_IMPORTED_CONFIG_RELEASE_DEBUG Release)
Everything compiles and links fine, but when it comes to run the application I need to copy the dlls into my working directory.
Currently, I'm doing it this way:
get_filename_component(QT_DLL_DIR ${QT_QMAKE_EXECUTABLE} DIRECTORY)
set(THIRD_PARTY_BINARIES
"${EXTERNAL_DIR}/pthread/bin/pthreadVC3$<$<CONFIG:Debug>:d>.dll"
"${EXTERNAL_DIR}/openssl/bin/libcrypto-1_1-x64.dll"
"${EXTERNAL_DIR}/openssl/bin/libssl-1_1-x64.dll"
"${QT_DLL_DIR}/QtCore$<$<CONFIG:Debug>:d>${QT_VERSION_MAJOR}.dll"
"${QT_DLL_DIR}/QtGui$<$<CONFIG:Debug>:d>${QT_VERSION_MAJOR}.dll")
foreach(BINARY ${THIRD_PARTY_BINARIES})
add_custom_command(TARGET MyApp POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<SHELL_PATH:${BINARY}>" "$<SHELL_PATH:${OUTPUT_DIR}/$<CONFIG>>")
endforeach()
As you can see, a lot of insight about dll naming (version number, platform, debug information and whatsoever) is hardcoded in these lines.
In fact, I already set myself the dll paths for pthread target, so why should I do that again?
Manually getting each target property seems a bit cumbersome to me, but taking into account several configurations makes things almost impossible: I can only use generator expressions inside add_custom_command(), so I don't have any clue on how to do all this in a compact and elegant way.
Additionally, I tried the following:
get_target_property(PTHREAD_LOCATION pthread LOCATION)
message("pthread: ${PTHREAD_LOCATION}")
get_target_property(OPENSSL_LOCATION OpenSSL::SSL LOCATION)
message("openssl: ${OPENSSL_LOCATION}")
get_target_property(QT_LOCATION Qt4::QtCore LOCATION)
message("qt: ${QT_LOCATION}")
And got the following output:
pthread: pthread-NOTFOUND
openssl: D:/workspace/ewas_win_2015/externals/openssl/lib/libssl.lib
qt: D:/local/qt/qt-4.8.7/lib/QtCore4.lib
I tried the same with LOCATION_RELEASE and got the following:
pthread: D:/workspace/ewas_win_2015/externals/pthread/bin/pthreadVC3.dll
openssl: D:/workspace/ewas_win_2015/externals/openssl/lib/libssl.lib
qt: D:/local/qt/qt-4.8.7/lib/QtCore4.lib
And with LOCATION_DEBUG:
pthread: D:/workspace/ewas_win_2015/externals/pthread/bin/pthreadVC3d.dll
openssl: D:/workspace/ewas_win_2015/externals/openssl/lib/libssl.lib
qt: D:/local/qt/qt-4.8.7/lib/QtCored4.lib
Then I switched to IMPORTED_LOCATION:
pthread: PTHREAD_LOCATION-NOTFOUND
openssl: D:/workspace/ewas_win_2015/externals/openssl/lib/libssl.lib
qt: QT_LOCATION-NOTFOUND
And to IMPORTED_LOCATION_RELEASE:
pthread: D:/workspace/ewas_win_2015/externals/pthread/bin/pthreadVC3.dll
openssl: D:/workspace/ewas_win_2015/externals/openssl/lib/libssl.lib
qt: D:/local/qt/qt-4.8.7/lib/QtCore4.lib
And finally to IMPORTED_LOCATION_DEBUG:
pthread: D:/workspace/ewas_win_2015/externals/pthread/bin/pthreadVC3d.dll
openssl: OPENSSL_LOCATION-NOTFOUND
qt: D:/local/qt/qt-4.8.7/lib/QtCored4.lib
I've read somewhere that the dll path is supposed to be in LOCATION (or IMPORTED_LOCATION) property, but apparently it's not the case for openssl and qt.
Moreover, each library seems to behave its own way. How am I supposed to do this programmatically (also for other 3rd-party libraries that I may have in my project)?
Thanks in advance.

Related

CMake TARGET_RUNTIME_DLLS is empty

I have git cloned, built (with MSVC for both Debug and Release) and then installed wxWidgets:
cmake -B build wxWidgets
cmake --build build --config <CONFIG>
cmake --install build --prefix my_install --config <CONFIG>
with <CONFIG> = Debug and <CONFIG> = Release.
Then I used the following CMake script to link against it, as suggested by the wiki:
cmake_minimum_required(VERSION 3.16)
project(Test)
add_executable(Test WIN32 Main.cpp)
# wxWidgets
SET(wxWidgets_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/my_install)
find_package(wxWidgets COMPONENTS core base REQUIRED)
include(${wxWidgets_USE_FILE})
target_link_libraries(Test PRIVATE ${wxWidgets_LIBRARIES})
# Copy runtime DLLs to the directory of the executable.
add_custom_command(TARGET Test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Runtime Dlls: $<TARGET_RUNTIME_DLLS:Test>"
)
My goal is to automatically copy the DLLs into the directory of the built executable, so that they can be found at runtime. For that I'm using the TARGET_RUNTIME_DLLS generator expression (follwing the sample code in the docs). In the code above, I only print out the expression at build time for testing purposes. The problem is that it is empty.
The approach worked for me before when installing and linking SDL, but SDL provides package configuration files which create imported targets, defining the DLL location(s) via IMPORTED_LOCATION_RELEASE or IMPORTED_LOCATION_DEBUG. For wxWidgets one is apparently supposed to use the FindwxWidgets.cmake script shipped with CMake, which sadly doesn't define the produced binaries. Maybe that's why TARGET_RUNTIME_DLLS isn't populated.
Does anyone know, either how to get TARGET_RUNTIME_DLLS filled or how to obtain the list of built wxWidgets DLLs for the current configuration (Release/Debug) post build copying?
Thanks a lot in advance!
I am dealing with a similar problem.
First sanity checks:
You have to work on windows platform otherwise this feature does not
work.
Your Cmake is 3.21 or above
Next comes fuzzy part. I think the library that you are trying to include have to be a Shared Imported library and you have to set a set_target_properties for IMPORTED_IMPLIB which is a path to a .lib file of sort (dll import library, I think it is called) So you have to make sure that it is all set in the package library that you trying to link with your executable.
If you have those dll avaiable and you just want to use them and not actually build them then you can write your own cmake script that will do just what I said above. Then you can include that cmake file in your project and then link against your app.
Note: I also work on similar issue right now and what I just said have not been working very reliably. I got some dlls to be copied and some do not.
Edit:
Cmake docs give a more detailed explanation on how this library setting should look like if you use find_package feature.
Found here: https://cmake.org/cmake/help/latest/command/add_library.html#imported-libraries
An UNKNOWN library type is typically only used in the implementation
of Find Modules. It allows the path to an imported library (often
found using the find_library() command) to be used without having to
know what type of library it is. This is especially useful on Windows
where a static library and a DLL's import library both have the same
file extension.

Cmake cannot find wxWidgets on Windows

I have searched for this topic and found this and this, but it only seems to either pertain to building wxWidgets or do not contain an answer to my question.
I have built the static libs für wxWidgets on Windows successfully, but I am now struggling to correctly include the libraries to my project using Cmake. This is my CMakeLists.txt:
set(PROJECT_NAME wxapp)
project(${PROJECT_NAME})
cmake_minimum_required(VERSION 2.8)
set(SRC_LIST main.cpp app.cpp app.h frame.cpp frame.h)
add_executable(${PROJECT_NAME} WIN32 ${SRC_LIST})
find_package(wxWidgets REQUIRED net gl core base)
include(${wxWidgets_USE_FILE})
target_link_libraries(${PROJECT_NAME} ${wxWidgets_LIBRARIES})
set(wxWidgets_USE_LIBS ON)
set(wxWidgets_CONFIGURATION msw)
I have set the WXWIN path variable correctly. Yet, CMake throws an error with this configuration:
Could NOT find wxWidgets (missing: wxWidgets_LIBRARIES
wxWidgets_INCLUDE_DIRS net gl core base)
I have tried multiple suggestions, like using downloading prebuild dynamic libraries and adding them manually as suggested here, e.g.
set(wxWidgets_ROOT_DIR $ENV{WXWIN})
set(wxWidgets_LIBRARIES $ENV{WXWIN}/include)
set(wxWidgets_INCLUDE_DIR $ENV{WXWIN}/lib/vc14x_x64_dll)
include_directories(includes $ENV{WXWIN} $ENV{WXWIN}/include $ENV{WXWIN}/lib/vc14x_x64_dll)
link_directories($ENV{WXWIN} $ENV{WXWIN}/include $ENV{WXWIN}/lib/vc14x_x64_dll) # this seems to be a discouraged/deprecated method
but all to no avail.
WORKAROUND (at least it works for me):
I changed the find_package command from the suggested statement:
find_package(wxWidgets COMPONENTS gl core base OPTIONAL_COMPONENTS net)
to
find_package(wxWidgets 3.1 REQUIRED)
A user in this post had this included in his/her CMake file and out of deperation, I tried this and it actually worked. Seems like the configuration of CMake under certain unknown circumstances won't function correctly with the statement provided in the instructions. If I change the command to the new statement my static build as well as self- and precompiled dlls are flawlessly identifed by CMake.
I also cleared the CMake cache, inspired by this post. According to this post, there seems to be an issue with Windows and the CMake cache in some situations, but the reasons escape me. I could replicate the behaviour observed by the user there:
Under Windows, you have to point to the installation directory, e.g.
set(wxWidgets_ROOT_DIR "C:/wxWidgets-3.1.3") set(wxWidgets_LIB_DIR
"C:/wxWidgets-3.1.3/lib/vc14x_x64_dll")
But then the behavior is still strange. When i use CMake GUI (3.16,
latest), behavior is like this
delete cache (just to be consistent)
press 'configure' button
--> fail: CMake Error at C:/Program Files/CMake/share/cmake-3.16/Modules/FindPackageHandleStandardArgs.cmake:146
(message): Could NOT find wxWidgets (missing: wxWidgets_LIBRARIES
wxWidgets_INCLUDE_DIRS core base qa adv net html gl propgrid richtext)
press 'configure' button again
--> success
I also have observed some inconsistent behaviour of CMake within the IDEs CLion and CodeBlocks when clearing the cache and reloading the project.
So, exactly how the cache clearing plays into the resolution of my issue, I don't know. But if I merely clear the cache, reload my project and leave the CMake instruction at find_package(wxWidgets COMPONENTS gl core base OPTIONAL_COMPONENTS net), CMake won't find my installation despite the WXWIN path being set correctly.
If you know more about this, I am happy to be corrected.

Errors while making tdlib example

l am trying to build a java example for the td lib following the README(https://github.com/tdlib/td/tree/master/example/java)
I have got following mistackes. Please tell how can I fix it?
C:\Users\irina\td\jnibuild>cmake -DCMAKE_BUILD_TYPE=Debug -DTD_ENABLE_JNI=ON -DCMAKE_INSTALL_PREFIX:PATH=../example/java/td ..
-- Could NOT find ccache
-- Found OpenSSL: C:/OpenSSL-Win32/include optimized;C:/OpenSSL-Win32/lib/VC/ssleay32MD.lib;debug;C:/OpenSSL-Win32/lib/VC/ssleay32MDd.lib;optimized;C:/OpenSSL-Win32/lib/VC/libeay32MD.lib;debug;C:/OpenSSL-Win32/lib/VC/libeay32MDd.lib
-- Could NOT find ZLIB (missing: ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
-- Could NOT find ZLIB (missing: ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
CMake Warning at CMakeLists.txt:256 (message):
Not found zlib: skip TDLib, tdactor, tdnet, tddb
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
ZLIB_LIBRARY
linked by target "tdutils" in directory C:/Users/irina/td/tdutils
-- Configuring incomplete, errors occurred!
See also "C:/Users/irina/td/jnibuild/CMakeFiles/CMakeOutput.log".
See also "C:/Users/irina/td/jnibuild/CMakeFiles/CMakeError.log".
ZLIB for Windows is a part of the GnuWin32 project (I'm not sure whether it is allowed to give links on SO). As I see, the CMakeLists.txt uses find_package to lookup the ZLIB library:
if (NOT ZLIB_FOUND)
find_package(ZLIB)
endif()
if (NOT ZLIB_FOUND)
message(WARNING "Not found zlib: skip TDLib, tdactor, tdnet, tddb")
return()
endif()
How the find_package command works is well described in the official documentation:
The command has two modes by which it searches for packages: “Module” mode and
“Config” mode. Module mode is available when the command is invoked with the
above reduced signature. CMake searches for a file called Find<package>.cmake
in the CMAKE_MODULE_PATH followed by the CMake installation. If the file is
found, it is read and processed by CMake. It is responsible for finding the
package, checking the version, and producing any needed messages. Many find-
modules provide limited or no support for versioning; check the module
documentation. If no module is found and the MODULE option is not given the
command proceeds to Config mode.
I've had a look into FindZLIB.cmake on my Windows machine. The module uses the following path: ZLIB_ROOT and the following registry keys:
"[HKEY_LOCAL_MACHINE\SOFTWARE\GnuWin32\Zlib;InstallPath]"
"$ENV{PROGRAMFILES}/zlib
So, as I understand, if you install GnuWin32 using the installer, the HKLM key will be written down into the registry and CMake will be able to find the path to ZLIB. If you wish to use just the zip-archive, the ZLIB_ROOT parameter must be correctly specified when you run CMake:
cmake -DZLIB_ROOT=<PATH-to-your-unpacked-zlib> -D.....

Building SDL2_image as a CMake external project

I've been trying to create a CMake-based build-system for a project that is supposed to use SDL2_image library. I do not want to force user to install any libraries to the system to be able to build the project, so I took advantage of the CMake's ability to download and build dependencies (freetype, SDL2 and SDL2_image) from source code as External Projects.
Everything is fine with freetype and SDL2 (which both include CMakeLists.txt files out of the box), but I've ran out of ideas how to make it work for SDL2_image. CMake's external projects support custom configuration and building settings which I used in different variants with no success.
The CMake file itself can be found here, but the problematic part is this:
# SDL_image library
ExternalProject_Add(sdl2_image_project
URL https://www.libsdl.org/projects/SDL_image/release/SDL2_image-2.0.0.tar.gz
DEPENDS sdl2_project
PREFIX ${LIBS_DIR}/SDL2_image
CONFIGURE_COMMAND LDFLAGS=-L${SDL2_BIN} CFLAGS=-I${SDL2_SRC}/include SDL2_CONFIG=${SDL2_BIN}/sdl2-config <SOURCE_DIR>/configure --prefix=<INSTALL_DIR> --enable-shared=no
BUILD_COMMAND make
INSTALL_COMMAND ""
)
An error occurs while building sdl2_image_project. Some trivial research discovered that the error is generated by the undefined references to parts of libdl. Here is a tiny part of the hole error:
libtool: link: gcc -I/home/snikitin/_src/img_glypher/libs/SDL2/src/sdl2_project/include -I/usr/local/include/SDL2 -D_REENTRANT -o showimage showimage.o -Wl,-rpath -Wl,/usr/local/lib -pthread -L/home/snikitin/_src/img_glypher/libs/SDL2/src/sdl2_project-build ./.libs/libSDL2_image.a -L/usr/local/lib -lSDL2 -pthread
/home/snikitin/_src/img_glypher/libs/SDL2/src/sdl2_project-build/libSDL2.a(SDL_dynapi.c.o): In function `get_sdlapi_entry':
/home/snikitin/_src/img_glypher/libs/SDL2/src/sdl2_project/src/dynapi/SDL_dynapi.c:227: undefined reference to `dlopen'
I think the problem takes place due to the fact that linker tries to create a shared version of SDL2_image library while linking it to a static libSDL2.a. The thing is - if this is right - SDL2 building step creates both static and shared versions of itself so one would assume that linker would use libSDL2-2.0.so instead (I do not actually need a shared library - just the static one, but I do not know how to prevent the build system from trying to create it apart from passing --enable-shared=no to SDL2_image configure script, which does not help in this case).
After a lot of googling I've discovered that the possible source of the problem is that sdl2-config (which is called to get some flags for compiler during SDL_image building) may be called with wrong arguments and produces wrong cflags which confuse everything else. But I'm not sure that is the case and also I do not know how to influence sdl2_config call from CMake (configure --help does not seem to unveil any useful options for this situation).
I am running Ubuntu 14.04 x64 if it matters in any way. Would appreciate any advice!
Looks like you need to link some libraries like m and dl. It can be fixed by providing
custom sdl2-config file. Copy sdl2-config from extracted archive and substitute --libs result:
--libs)
echo -L${exec_prefix}/lib -Wl,-rpath,${libdir} -pthread -lSDL2 -lm -ldl
;;
Note that order is important (that's why just modifying LIBS not works for me).
Now this file can be used in your ExternalProject_Add command instead of SDL2_CONFIG=${SDL2_BIN}/sdl2-config:
...
... CFLAGS=-I${SDL2_SRC}/include SDL2_CONFIG=${CMAKE_CURRENT_LIST_DIR}/sdl2-config <SOURCE_DIR>/configure
...

making conditions for linux and windows when linking libraries

Windows VC++ 2008
linux gcc 4.4.3
I have the following problem. When I compile on windows I need the ws2_32 library. However, when I compile on linux, I don't need to link this.
My CMakeLists.txt
INCLUDE_DIRECTORIES($CLIENT_SERVER_SOURCE_DIR/client)
INCLUDE_DIRECTORIES($CLIENT_SERVER_SOURCE_DIR/cltsvr_ults)
# Link the library
LINK_DIRECTORIES($CLIENT_SERVER_DIR/client)
# Add the executable
ADD_EXECUTABLE(clt test_clt)
# Link the executable to the client library
IF(WIN32)
TARGET_LINK_LIBRARIES(clt client ws2_32)
ENDIF(WIN32)
IF(CMAKE_COMPILER_IS_GNUCXXX)
TARGET_LINK_LIBRARIES(clt client)
ENDIF(CMAKE_COMPILER_IS_GNUCXXX)
I have tried unsuccessfully to compile under linux. Using the above conditions. However, It always tries to link the ws2_32 and I get a compile error. I think that the conditions aren't working, as it always falls through the WIN32 condition.
many thanks for any suggestions,
Since the WIN32 thing is such a fundamental part of CMake, I'd guess that there is more to this than what you mention.
Are you doing a clean check out of your code, or just copying up a whole directory on Linux? If you have all your CMake build files cached from the Windows build, maybe (just maybe!) something has snuck in there and "detects" itself as WIN32 on Linux?
Are you sure it is that line and not something else that causes the link to the stray Win-library? Maybe try a MESSAGE(STATUS "I am here")line within the IF(WIN32) just to make sure.
Are you sure the error is caused by linking that library? I can see a typo in your script, it should be IF(CMAKE_COMPILER_IS_GNUCXX) - you have an extra X on there. Perhaps you are not linking in what you thing you are, and that is why it fails.