CMake/CPack is inserting dynamic links instead of regular files for external shared libraries - cmake

I need to use an specific external shared library on a project. It links fine, and locally it runs like a charm.
The problem is when I need to make a distribution package. When I run cpack the output file has the symbolic links to these external shared libraries. I need it to include the external libraries as well (not just the symbolic link). How can I do it?
Here is my CMakeLists.txt
project(test VERSION 1.0.0 LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 11)
set(Protobuf_LIBRARIES $ENV{CONDA_PREFIX}/lib/libprotobuf.so)
add_executable(test main.cpp)
target_link_libraries(test
${Protobuf_LIBRARIES}
)
include(GNUInstallDirs)
install(TARGETS test
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(FILES ${Protobuf_LIBRARIES}
DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RuntimeLibraries
)
set(CPACK_PACKAGE_NAME "Test")
include(CPack)
=== EDIT ===
Following #alex-reinking suggestion, and for the sake of completition on this question, here is the CMakeLists.txt working. It still has a problem with dependencies pointing to local dynamic libraries, so I needed to use an external app to solve that on OSX ( https://github.com/auriamg/macdylibbundler/ ). Be aware that, on this example, I'm including a Linux library.
project(test VERSION 1.0.0 LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 11)
set(Protobuf_LIBRARIES $ENV{CONDA_PREFIX}/lib/libprotobuf.so)
add_executable(test main.cpp)
target_link_libraries(test
${Protobuf_LIBRARIES}
)
include(GNUInstallDirs)
install(TARGETS test
RUNTIME_DEPENDENCY_SET A-dependency
)
install(RUNTIME_DEPENDENCY_SET A-dependency PRE_EXCLUDE_REGEXES )
install(FILES ${MYLIB_LOCATION} DESTINATION ${CMAKE_INSTALL_LIBDIR})
set(CPACK_PACKAGE_NAME "Test")
include(CPack)```

Related

How can I make CPACK include 3rd party DLLs into the installer?

I've written a CMakeLists.txt as shown below:
cmake_minimum_required (VERSION 3.22)
project(tutorial)
set(wxWidgets_CONFIGURATION mswu)
find_package(wxWidgets REQUIRED COMPONENTS net core base)
include(${wxWidgets_USE_FILE})
add_executable(tutorial main.cpp)
target_link_libraries(tutorial ${wxWidgets_LIBRARIES})
install(TARGETS tutorial DESTINATION bin)
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "1")
include(CPack)
As you might guess, this is WxWidgets application.
Look at the bottom. It includes InstallRequiredSystemLibraries and CPack.
With this cmake I have generated a NSIS Cpack installer "tutorial-0.1.1-win64.exe".
But this installer only installs the project binary and some runtime libraries. It doesn't install any WxWidgets library. So the project binary fails to run on other systems that is not installed WxWidgets libraries.
I'd like to make NSIS Cpack installer installs WxWidgets DLL libraries also.
How can I make it?
See! Those binaries are needed to be installed! But NSIS Cpack installer doesn't install wxbase315ud_vc14x_x64.dll and wxmsw315ud_core_vc14x_x64.dll
I am working on Windows 10
I don't want to statically link to the WxWidgets libraries. I'd like to make it link dynamically with shared libraries.
There exists a previous talk for the similar subject :
Including external libraries in cpack output
I tried this way and it works :
cmake_minimum_required (VERSION 3.22)
project(tutorial)
if(CMAKE_BUILD_TYPE MATCHES Release)
message("release mode")
set(wxWidgets_CONFIGURATION mswu)
else()
message("debug mode")
set(wxWidgets_CONFIGURATION mswud)
endif()
find_package(wxWidgets REQUIRED COMPONENTS net core base)
include(${wxWidgets_USE_FILE})
add_executable(tutorial main.cpp)
target_link_libraries(tutorial ${wxWidgets_LIBRARIES})
install(TARGETS tutorial DESTINATION bin)
if(CMAKE_BUILD_TYPE MATCHES Release)
install(FILES ${wxWidgets_LIB_DIR}/wxbase315u_vc_custom.dll DESTINATION bin)
install(FILES ${wxWidgets_LIB_DIR}/wxmsw315u_core_vc_custom.dll DESTINATION bin)
else()
install(FILES ${wxWidgets_LIB_DIR}/wxbase315ud_vc_custom.dll DESTINATION bin)
install(FILES ${wxWidgets_LIB_DIR}/wxmsw315ud_core_vc_custom.dll DESTINATION bin)
endif()
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "1")
include(CPack)
Look I added this below :
if(CMAKE_BUILD_TYPE MATCHES Release)
install(FILES ${wxWidgets_LIB_DIR}/wxbase315u_vc_custom.dll DESTINATION bin)
install(FILES ${wxWidgets_LIB_DIR}/wxmsw315u_core_vc_custom.dll DESTINATION bin)
else()
install(FILES ${wxWidgets_LIB_DIR}/wxbase315ud_vc_custom.dll DESTINATION bin)
install(FILES ${wxWidgets_LIB_DIR}/wxmsw315ud_core_vc_custom.dll DESTINATION bin)
endif()
And It works as I wanted!

Cannot specify compile options for imported target "..."

I want to provide the users of my library with two targets: one that specifies the include path etc., and one that carries useful extra compile options. However, for the extra target some of my users are getting the error
Cannot specify compile options for imported target "myproject::extra"
so it seems on older CMake versions.
I tested with CMake 3.9.2. The test project, including CI is on GitHub, with failing build here.
(How) can my approach be rendered robust for all CMake versions?
The project's main CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(myproject)
add_library(myproject INTERFACE)
set(MYPROJECT_VERSION "1.0.0")
target_include_directories(myproject INTERFACE
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION include)
install(TARGETS myproject EXPORT myproject-targets)
install(EXPORT myproject-targets FILE myprojectTargets.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/myproject")
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/myprojectConfigVersion.cmake" VERSION ${MYPROJECT_VERSION} COMPATIBILITY AnyNewerVersion)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/myprojectConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/myprojectConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/myproject")
The project's myprojectConfig.cmake:
include(CMakeFindDependencyMacro)
if(NOT TARGET myproject)
include("${CMAKE_CURRENT_LIST_DIR}/myprojectTargets.cmake")
endif()
if(NOT TARGET myproject::extra)
add_library(myproject::extra INTERFACE IMPORTED)
if(MSVC)
target_compile_options(myproject::extra INTERFACE /W4)
else()
target_compile_options(myproject::extra INTERFACE -Wall)
endif()
endif()
The user's project CMakeLists.txt could then look as follows:
cmake_minimum_required(VERSION 3.0)
project(myexec)
find_package(myproject REQUIRED)
add_executable(myexec main.cpp)
target_link_libraries(myexec PRIVATE myproject myproject::extra)
List of functions applicable for IMPORTED and INTERFACE targets changes as CMake evolves.
Most of such functions affects only on specific target properties. So, instead of calling a function, you may set the property directly. This will work in any CMake version:
# Works only in new CMake versions
target_compile_options(myproject::extra INTERFACE /W4)
# Equivalent which works in any CMake version
set_property(TARGET myproject::extra PROPERTY INTERFACE_COMPILE_OPTIONS /W4)

Why does CMAKE installed target fail to link to provided libraries?

I'm building a target that depends on some provided libraries. My src directory hierarchy looks like this:
I use the following CMakeLists.txt to build a target and install it within the build/install directory:
cmake_minimum_required(VERSION 2.8.3)
project(example)
include_directories(include)
link_directories(lib)
add_executable(${PROJECT_NAME}
src/example.cpp)
target_link_libraries(${PROJECT_NAME}
curlpp)
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)
install(TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES lib/libcurlpp.a lib/libcurlpp.so lib/libcurlpp.so.1 lib/libcurlpp.so.1.0.0 DESTINATION deps)
When I do a simple build, everything is fine and the target is properly linked to the provided libraries. But when I do make install, the target is generated but fails to link to the libraries:
I understand the linkage failure of the installed target: the install/deps directory is not in the LD_LIBRARY_PATH of my environment. But what has cmake done to make the directly built target link correctly? Can I do something similar to make the installed target work properly?
Here is a minimal replication of the problem

target_link_libraries erroneously pulls in MODULE library at link time

I have two C++ projects using CMake 3.12.2.
The first one is MODULE library (a dynamically loaded plugin). It installs a DLL, a header file and the configuration file for CMake.
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(MyPlugin LANGUAGES CXX)
set(CMAKE_DEBUG_POSTFIX "d")
# MODULE libraries are dynamically loaded at runtime and never linked against
add_library(MyPlugin MODULE
include/a.h
src/a.cpp
src/b.h
src/b.cpp
)
target_include_directories(MyPlugin
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/MyPlugin>
PRIVATE
src
)
install(TARGETS MyPlugin EXPORT MyPluginConfig
# MODULE libraries are installed as LIBRARY
LIBRARY DESTINATION plugins COMPONENT Runtime
RUNTIME DESTINATION bin COMPONENT Runtime
PUBLIC_HEADER DESTINATION include/MyPlugin COMPONENT Development
)
install(FILES $<TARGET_PDB_FILE:MyPlugin> DESTINATION plugins OPTIONAL COMPONENT Runtime)
install(
DIRECTORY include/
DESTINATION include/MyPlugin
FILES_MATCHING PATTERN "*.h"
)
install(EXPORT MyPluginConfig
NAMESPACE MyPlugin::
DESTINATION lib/cmake/MyPlugin
)
The second one is a simple executable which pulls in the header file of the plugin via target_link_libraries (the modern CMake way).
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(MyExe LANGUAGES CXX)
set(CMAKE_DEBUG_POSTFIX "d")
find_package(MyPlugin REQUIRED)
add_executable(MyExe src/main.cpp)
target_link_libraries(MyExe MyPlugin::MyPlugin)
Using the vs2015 generated solution the link fails because the plugin DLL is fed during the link of the executable...
Has anyone a solution for this or should I file a bug?
Regards.
A solution to this issue is to split the library in two: an interface library which only provides the headers and a module library which does not export any header.
The module library CMakeLists.txt becomes:
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(MyPlugin LANGUAGES CXX)
set(CMAKE_DEBUG_POSTFIX "d")
# Move public headers to a dedicated INTERFACE library
add_library(MyPluginInterface INTERFACE)
add_custom_target(Includes SOURCES include/a.h)
target_include_directories(MyPluginInterfacecmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(MyPlugin LANGUAGES CXX)
set(CMAKE_DEBUG_POSTFIX "d")
# Move public headers to a dedicated INTERFACE library
add_library(MyPluginInterface INTERFACE)
add_custom_target(Includes SOURCES include/a.h)
target_include_directories(MyPluginInterface
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/MyPlugin>
)
install(TARGETS MyPluginInterface EXPORT MyPluginConfig
PUBLIC_HEADER DESTINATION include/MyPlugin COMPONENT Development
)
# MODULE libraries are dynamically loaded at runtime and never linked against
add_library(MyPlugin MODULE
src/a.cpp
src/b.h
src/b.cpp
)
target_link_libraries(MyPlugin MyPluginInterface)
install(TARGETS MyPlugin
# MODULE libraries are installed as LIBRARY
LIBRARY DESTINATION plugins COMPONENT Runtime
RUNTIME DESTINATION bin COMPONENT Runtime
)
install(FILES $<TARGET_PDB_FILE:MyPlugin> DESTINATION plugins OPTIONAL COMPONENT Runtime)
install(
DIRECTORY include/
DESTINATION include/MyPlugin
FILES_MATCHING PATTERN "*.h"
)
install(EXPORT MyPluginConfig
NAMESPACE MyPlugin::
DESTINATION lib/cmake/MyPlugin
)
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/MyPlugin>
)
install(TARGETS MyPluginInterface EXPORT MyPluginConfig
PUBLIC_HEADER DESTINATION include/MyPlugin COMPONENT Development
)
# MODULE libraries are dynamically loaded at runtime and never linked against
add_library(MyPlugin MODULE
src/a.cpp
src/b.h
src/b.cpp
)
target_link_libraries(MyPlugin MyPluginInterface)
install(TARGETS MyPlugin
# MODULE libraries are installed as LIBRARY
LIBRARY DESTINATION plugins COMPONENT Runtime
RUNTIME DESTINATION bin COMPONENT Runtime
)
install(FILES $<TARGET_PDB_FILE:MyPlugin> DESTINATION plugins OPTIONAL COMPONENT Runtime)
install(
DIRECTORY include/
DESTINATION include/MyPlugin
FILES_MATCHING PATTERN "*.h"
)
install(EXPORT MyPluginConfig
NAMESPACE MyPlugin::
DESTINATION lib/cmake/MyPlugin
)
The executable CMakeLists.txt becomes:
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(MyExe LANGUAGES CXX)
set(CMAKE_DEBUG_POSTFIX "d")
find_package(MyPlugin REQUIRED)
add_executable(MyExe src/main.cpp)
target_link_libraries(MyExe MyPlugin::MyPluginInterface)

How to search for libraries in multiplatform cmake builds?

I am searching for a nice way to handle linked libs in cmake. In my case the cmake configuration file(CMakeLists.txt) is executed in Linux environments and on cygwin on Windows.
The build process contains two libs. libA is build from sources and only depends on the std. C API. libB is build, as well, from sources and includes the libA. The libs are build separately(with an own git repro).
By defining in cmake at "libB/CMakeLists.txt"
find_library (libB A)
I am linking libB against libA. When linking under cygwin, this line fails. When I change it to
find_library (libB ${CMAKE_LIBRARY_PATH}/static/libA.dll.a)
it works.
Both libs are build as shared libs.
"libA/CMakeLists.txt":
add_library(libA SHARED ${libA_source})
install(
TARGETS libA
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/static
)
and
"libB/CMakeLists.txt":
add_library(libB SHARED ${libB_source})
install(
TARGETS libB
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/static
)
Under Linux this works fine. The libs are placed into "/user/local/lib". Under Windows the libs are placed into "/user/local/lib/shared" + the dlls are placed into "/user/local/bin".
The statement to link libB against libA changes to ("libB/CMakeLists.txt"):
if (UNIX)
find_library (libB A)
elseif (CYGWIN)
find_library (libB ${CMAKE_LIBRARY_PATH}/static/libA.dll.a)
endif()
Any idea how to handle the linker references of these two builds in a simple - maybe - platform independent line?
This should work within your libB/CMakeLists.txt:
find_library(libA
NAMES A libA libA.so libA.dll libA.dll.a
HINTS ${CMAKE_INSTALL_FULL_LIBDIR}
)
target_link_libraries(libB ${libA})
Check the path to libA with
message(STATUS "libA=${libA}")