CMake - install third party dll dependency - c++

I'm using a pre-compiled third party library that has multiple DLLs (one for the actual third party, and some more as its own dependencies)
My Directory structure is as follows
MyApp
CMakeLists.txt // Root CMake file
src
MyCode.cpp
thirdpartydep // Precompiled thirdparty dependency
FindThirdPartyDep.cmake
bin/
thirdparty.dll
thirdparty_dep1.dll
thirdparty_dep2.dll
include/
thirdparty.h
lib/
thirdparty.lib // this is the importlibrary that loads thirdparty.dll
Until now, we've been copying over all the DLLs in the thirdpartydep/bin directory using copy_if_different and manually listing the paths to the DLLs. I'm trying to set up the install target correctly to copy the dlls in thirdpartydep/bin to CMAKE_INSTALL_PREFIX/bin but I can't figure out how to tell cmake about the extra binary files belonging to thirdpartydep.

If you use modern CMake with correctly build CONFIG thirdparty packages (* -config.cmake) instead of MODULES (Find*.cmake), then this will work:
MACRO(INSTALL_ADD_IMPORTED_DLLS target_list target_component destination_folder)
foreach(one_trg ${target_list})
get_target_property(one_trg_type ${one_trg} TYPE)
if (NOT one_trg_type STREQUAL "INTERFACE_LIBRARY")
get_target_property(one_trg_dll_location ${one_trg} IMPORTED_LOCATION_RELEASE)
if( one_trg_dll_location MATCHES ".dll$")
install(FILES ${one_trg_dll_location} DESTINATION ${destination_folder} CONFIGURATIONS Release COMPONENT ${target_component})
endif()
get_target_property(one_trg_dll_location ${one_trg} IMPORTED_LOCATION_DEBUG)
if( one_trg_dll_location MATCHES ".dll$")
install(FILES ${one_trg_dll_location} DESTINATION ${destination_folder} CONFIGURATIONS Debug COMPONENT ${target_component})
endif()
endif()
endforeach()
ENDMACRO()
it is used like this:
set(THIRDPARTY_TARGETS "example_target1 example_target2 opencv_core")
if(MSVC)
INSTALL_ADD_IMPORTED_DLLS("${THIRDPARTY_TARGETS}" bin bin)
endif()

You can simply use the install() command like that:
install(FILES ${DLL_PATH} DESTINATION ${CMAKE_INSTALL_PREFIX}/dll
CONFIGURATIONS Release COMPONENT ${LIBRARY_NAME})

Related

Deplying a C++ application on Linux- linking everything statically to simplify deployment?

I am building a C++ project from Github and want to deploy the code to a remote Linux machine. This is all new to me.
The project has a main.cpp, which includes the various headers/sources like a library.
The CMake outputs an executable (to represent main.cpp) AND a separate static library. The project also uses OpenSSL, which I have linked statically.
I presume the OpenSSL functions are included within the static library? So when I deploy, I don't need to copy-over or install any OpenSSL on the remote machine?
Is it possible to modify the CMake so the application and the library are merged in to one file?
I am trying to make deployment as simple as copying over a single file, if this is possible.
Any additional advice/references are most-welcome.
UPDATE the CMake script:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(helloworld C CXX)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_BUILD_TYPE Release)
set (BUILD_MAIN TRUE)
set (BUILD_SHARED_LIBS FALSE)
set (OPENSSL_USE_STATIC_LIBS TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set( HELLOWORLD_HEADERS helloworld/File1.h helloworld/File2.h )
set( HELLOWORLD_SOURCES helloworld/File1.cpp helloworld/File2.cpp )
# Static library
add_library( helloworld ${HELLOWORLD_SOURCES} ${HELLOWORLD_HEADERS} )
# Rapidjson
include_directories(/tmp/rapidjson/include/)
# OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(helloworld PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
target_link_libraries(helloworld PRIVATE ${OPENSSL_LIBRARIES})
set( HELLOWORLD_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
include(GNUInstallDirs)
target_include_directories(helloworld PUBLIC
$<BUILD_INTERFACE:${HELLOWORLD_INCLUDE_DIRS}/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/helloworld>
)
set_target_properties(helloworld PROPERTIES PUBLIC_HEADER "${HELLOWORLD_HEADERS}")
add_library(helloworld::helloworld ALIAS helloworld)
option(HELLOWORLD_INSTALL "Install HelloWorld" TRUE)
if (HELLOWORLD_INSTALL)
install(TARGETS helloworld
EXPORT helloworld
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/helloworld/
)
configure_file("${CMAKE_CURRENT_LIST_DIR}/helloworld-config.cmake.in" "${CMAKE_BINARY_DIR}/helloworld-config.cmake" #ONLY)
install(FILES "${CMAKE_BINARY_DIR}/helloworld-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/helloworld")
install(EXPORT helloworld
FILE helloworld-targets.cmake
NAMESPACE helloworld::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/helloworld
)
endif()
if (BUILD_MAIN)
add_executable(main main.cpp)
target_link_libraries(main helloworld)
endif()
ITNOA
I it is very helpful to make URL of your GitHub's project, but I write some public notes about that
In generally in CMake for static linking your library to your executable, you can write simple like below (from official CMake example)
add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)
In above example your executable file is just work without needing .a library file and you can simple copy single file.
if you want to make all of thing static, you make sure all dependencies make static link to your project, like CMake: how to produce binaries "as static as possible"
if you want to prevent library creation, Probably in your CMake file, you can find add_library command, and add_executable command. you can remove add_library command and add all sources to add_executable command.
for example add_executable(a.out main.cpp lib.cpp)

CMake import local shared library and how to include it in a header

I'm stuck to import a shared library in another project with CMake.
my-utils-lib
My lib files are generated in my-utils-lib project:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(my-utils-lib VERSION 1.0.0 DESCRIPTION "Utils for C++.")
set(CMAKE_CXX_STANDARD 14)
add_library(
my-utils-lib SHARED
./src/string_utils/find_matches.cpp
./src/string_utils/split.cpp
./src/string_utils/format.cpp
./src/vector_utils/print_vector.cpp
)
set_target_properties(
my-utils-lib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
)
target_include_directories(my-utils-lib PRIVATE src)
include(GNUInstallDirs)
install(
TARGETS my-utils-lib
DESTINATION ${CMAKE_INSTALL_LIBDIR}
EXPORT my-utils-lib
)
configure_file(my-utils-lib.pc.in my-utils-lib.pc #ONLY)
install(
FILES ${CMAKE_BINARY_DIR}/my-utils-lib.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig
)
I run mkdir build && cd ./build/ && cmake .. && sudo make install to generate and install my library files.
Finally, my-utils-lib project tree looks like this:
CMakeLists.txt
my-utils-lib.pc.in
src/
|_main.cpp
|_main.h
|_string_utils/
|_find_matches.cpp
|_find_matches.h
|_format.cpp
|_format.h
|_split.cpp
|_split.h
|_vector_utils/
|_print_vector.cpp
|_print_vector.h
my-project
In another project, I'd like to import "split.h" in one of my local headers without having to enter the full path to the actual file. Something like:
#include "my-utils-lib/string_utils/split.h"
I tried many solution so far, none of them is working. My last attempt is:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(longer_sub_sequence)
set(CMAKE_CXX_STANDARD 14)
find_package(Qt5Widgets REQUIRED)
find_library(my-utils-lib libmy-utils-lib.1.0.0.dylib)
add_library(my-utils-lib SHARED IMPORTED)
add_executable(
longer_sub_sequence
main.cpp
visualization/draw_matrix.cpp
visualization/draw_matrix.h
)
target_link_libraries(longer_sub_sequence my-utils-lib Qt5::Widgets)
CMake autobuild runs fine (I am using CLion as an IDE) but I can't find how to import any code from my library.
I checked in /usr/local/lib (where CMake generated my lib files), and found the correct lib files :
find . -maxdepth 1 -name "*my-utils-lib*" -print
./libmy-utils-lib.1.0.0.dylib
./libmy-utils-lib.1.dylib
./libmy-utils-lib.dylib
What am I missing here ?
You are missing to install the library header.
You should add an install command for header just like you did with the library binary.
Moreover you should consider using comparison signs (< and >) instead of double quote for include directive :
#include <my-utils-lib/string_utils/split.h>
One last remark, you are generating and installing a .pc file which is used by pkg-config only and you're not using it.

Install and export INTERFACE only library, CMake

new CMake user here.
I've made a simple header only library, with the following CMake file:
cmake_minimum_required(VERSION 3.7)
project(mylib VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
#add_compile_options(-Wa -aslh)
# Define the library target
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE include/)
add_executable(mytest test/basic_checks.cpp)
target_link_libraries(mytest mylib)
From http://foonathan.net/blog/2016/03/03/cmake-install.html
I've learned there are several steps to getting a library installed and to have it useable from another CMake project. First it must be "installed" and then it must be "exported". Then there's find_package, but I'll cross that bridge after I've gotten over the first two steps.
So following the example, I figure that I could add the following to the bottom of my CMake file:
# Install the header file.
install(FILES include/mylib.hpp DESTINATION "include/mylib-${PROJECT_VERSION}")
But the guide talks about also using install on TARGETS, and then adding EXPORT to the target install commands.
How much of this applies to a header only library in which I have no other compiled code of files other than the header?
How do I apply the steps described in : http://foonathan.net/blog/2016/03/03/cmake-install.html
To an INTERFACE only library? The lack of .cpp files in my project is leading me to question which instructions apply and don't apply.
try this:
cmake_minimum_required(VERSION 3.7)
project(mylib VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
#add_compile_options(-Wa -aslh)
# Define the library target
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/mylibConfigVersion.cmake"
VERSION 0.1
COMPATIBILITY AnyNewerVersion
)
install(TARGETS mylib
EXPORT mylibTargets
LIBRARY DESTINATION lib COMPONENT Runtime
ARCHIVE DESTINATION lib COMPONENT Development
RUNTIME DESTINATION bin COMPONENT Runtime
PUBLIC_HEADER DESTINATION include COMPONENT Development
BUNDLE DESTINATION bin COMPONENT Runtime
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/mylibConfig.cmake.in"
"${PROJECT_BINARY_DIR}/mylibConfig.cmake"
INSTALL_DESTINATION lib/cmake/mylib
)
install(EXPORT mylibTargets DESTINATION lib/cmake/mylib)
install(FILES "${PROJECT_BINARY_DIR}/mylibConfigVersion.cmake"
"${PROJECT_BINARY_DIR}/mylibConfig.cmake"
DESTINATION lib/cmake/mylib)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include)
add_executable(mytest test/basic_checks.cpp)
target_link_libraries(mytest mylib)
the content of cmake/mylibConfig.cmake.in should only be this
#PACKAGE_INIT#
include("${CMAKE_CURRENT_LIST_DIR}/mylibTargets.cmake")
check_required_components("#PROJECT_NAME#")
if you do all of this, not only that it makes your header-only library 'installable', but it also makes it 'findable'. users will be able to import your library like so:
find_package(mylib CONFIG REQUIRED)
target_link_libraries(MyApp mylib) # installed include/ path automatically added
Just a small addition to https://stackoverflow.com/a/49143782/2119944
Probably cmake/mylibConfig.cmake.in should look like this:
#PACKAGE_INIT#
include("${CMAKE_INSTALL_PREFIX}/lib/cmake/mylib/mylibTargets.cmake")
check_required_components("#PROJECT_NAME#")
Otherwise it will point to local project dir.

Integrate CMake subproject with another

I've wrote a C++ library MyLib and I'd like to integrate it with another project ExternPro. So in ExternPro I wrote CMakeLists.txt like this:
add_subdirectory(MyLib)
ADD_EXECUTABLE(test test.cpp)
include_directories(${MyLib_INCLUDE_DIRS})
target_link_libraries(test ${MyLib_LIBRARIES})
To set variables MyLib_LIBRARIES and MyLib_INCLUDE_DIRS I wrote:
set(MyLib_LIBRARIES ${PROJECT_SOURCE_DIR}/src/MyLib.a CACHE INTERNAL "")
set(MyLib_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include CACHE INTERNAL "")
But something wrong "No rule to make target MyLib/src/MyLib.a, needed by test. Stop."
So my question is, how should I wrote CMakeLists.txt correctly so cmake could help me build MyLib first and then take care of dependencies of ExternPro?
If those are two separate projects, I normally use "CMake find scripts" to reference one library from the other: http://www.vtk.org/Wiki/CMake:How_To_Find_Libraries#Writing_find_modules
But I normally use slightly different find script than described there (FindMyLibrary.cmake):
# Find MyLibrary installation
#
# This module needs following variables specified (e.g. through cmake -Dvar=)
# MyLibrary_ROOT_DIR - root directory of the library installation
#
# This module defines the following variables:
# MyLibrary_INCLUDE_DIRS - Where to find the public headers
# MyLibrary_LIBRARIES - List of mandatory and optional libraries
# MyLibrary_FOUND - True if an installation was found
#
# Configuration variables for tis module:
# MyLibrary_USE_STATIC_LIBS - Set to ON to force the use of the static
# libraries. Default is OFF.
# If MyLibrary_ROOT_DIR was defined in the environment, use it.
if(NOT MyLibrary_ROOT_DIR AND NOT $ENV{MyLibrary_ROOT_DIR} STREQUAL "")
set(MyLibrary_ROOT_DIR $ENV{MyLibrary_ROOT_DIR})
endif()
if(NOT MyLibrary_ROOT_DIR)
set(MyLibrary_ROOT_DIR /usr)
endif()
message(STATUS "Using MyLibrary_ROOT_DIR: ${MyLibrary_ROOT_DIR}")
find_path(MyLibrary_INCLUDE_DIRS
NAMES mylib/mylib.hpp
PATHS ${MyLibrary_ROOT_DIR}
PATH_SUFFIXES include)
# Here we set the default components
if(NOT MyLibrary_FIND_COMPONENTS)
set(MyLibrary_FIND_COMPONENTS mylibrary)
endif()
# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
if(MyLibrary_USE_STATIC_LIBS)
set(_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
endif()
endif()
foreach(COMPONENT ${MyLibrary_FIND_COMPONENTS})
find_library(MyLibrary_${COMPONENT}_LIBRARY
NAMES ${COMPONENT}
HINTS ${MyLibrary_ROOT_DIR}
PATH_SUFFIXES lib64 lib
NO_DEFAULT_PATH)
set(MyLibrary_LIBRARIES ${MyLibrary_LIBRARIES} ${MyLibrary_${COMPONENT}_LIBRARY})
endforeach()
# Restore the original find library ordering
if(MyLibrary_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_mylib_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
# handle the QUIETLY and REQUIRED arguments and set MyLibrary_FOUND to
# TRUE if all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
MyLibrary "Could NOT find MyLibrary: set MyLibrary_ROOT_DIR to a proper location"
MyLibrary_LIBRARIES
MyLibrary_INCLUDE_DIRS)
mark_as_advanced(MyLibrary_INCLUDE_DIRS MyLibrary_LIBRARIES)
Then used like this:
find_package(MyLibrary REQUIRED)
include_directories(SYSTEM ${MyLibrary_INCLUDE_DIRS})
target_link_libraries(${TARGET}
${MyLibrary_LIBRARIES}
)
Basically, it goes like this:
The library is built and installed (make install) to either the default location (e.g. /usr), or some other location (usual in development).
The FindMyLibrary.cmake is part of the library installation (for RPM the library devel package) and installs as well (into ${instdir}/share/cmake/Modules, for example).
The dependent project then adds the path to the CMAKE_MODULE_PATH and uses the find script to find the public headers and libraries as installed.
The advantage is that this way you can either use it during the development (when you have the library sources and build the library), or without the library sources as well (with just the library and headers - the devel package - installed in the system).
Similar technique is normally used by Boost etc. (the find scripts provided already by the CMake).
Instead of path to the library, use library target for target_link_libraries.
Assuming your library project contains
add_library(MyLib ...)
Linking of executable in main project should be performed with
target_link_libraries(test MyLib)
First of all this doesn't work because variables set in a subdirectory are not set for the parent directory.
So to solve this properly you should define MyLib like:
add_library(MyLib ...)
target_include_directories(MyLib INTERFACE ${PROJECT_SOURCE_DIR}/include)
And for the ExternPro you just need to link to MyLib:
target_link_libraries(test MyLib)
This will automatically add the include directory to test and link MyLib properly.

Right way of creating a library, installing it and linking to another project using CMake

I have a set of files that I want to make into a library and then use that library in another project. This it how it looks like right now
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
SET(CMAKE_CXX_FLAGS "-std=c++11 -fopenmp -fPIC")
add_library (helperlibs lib1.cpp lib2.cpp lib3.cpp lib4.cpp )
INSTALL(TARGETS helperlibs
DESTINATION "${HOME}/lib"
)
INSTALL(FILES lib1.h lib2.h lib3.h lib4.h helperheader.h
DESTINATION "${HOME}/include/helperlibs"
)
In this code Lib4 depends on Lib1-3 and Lib3 depends on Lib1-2 and Lib2 depends on Lib1. Each of these cpp files also depend on a helperheader.h file that contains some definitions and structs.
In my project I have the following CMake file
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
SET( CMAKE_CXX_FLAGS "-std=c++11 -fopenmp -fPIC")
SET(MYINCS ${HOME}/include/helperlibs)
SET(MYLIBDIR ${HOME}/lib)
SET(MYLIBS ${MYLIBDIR}/libhelperlibs.a )
include_directories(${MYINCS})
add_executable(main main.cpp)
target_link_libraries(main ${MYLIBS})
So what I am wondering if you want to create a static library and link to from a another project using cmake is this the way you should write?
Embed the search paths into the library target as properties and create an export.
This way executables in the same build tree will find the library and its include files without you having to specify paths (they become implicit).
I needed to read the cmake documentation carefully a few times before it dawned on me how it should work.
http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html#creating-packages
http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html
excerpt from a live example:
add_library(trustportal-util ${CMAKE_CURRENT_LIST_FILE} ${_source_files} ${_disabled_source_files} )
target_link_libraries(trustportal-util ${Boost_LIBRARIES})
if(APPLE)
find_library(SECURITY_FRAMEWORK Security)
target_link_libraries(trustportal-util ${SECURITY_FRAMEWORK})
else()
find_library(LIB_SSL ssl)
find_library(LIB_CRYPTO crypto)
target_link_libraries(trustportal-util ${LIB_SSL} ${LIB_CRYPTO})
endif()
target_compile_definitions(trustportal-util PUBLIC BOOST_MOVE_USE_STANDARD_LIBRARY_MOVE)
target_include_directories(trustportal-util PUBLIC ${Boost_INCLUDE_DIRS})
target_include_directories(trustportal-util PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_include_directories(trustportal-util SYSTEM PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS trustportal-util
EXPORT trustportal-utilExport
DESTINATION lib
INCLUDES DESTINATION include
)
INSTALL(EXPORT trustportal-utilExport DESTINATION lib)
One option is to do what you are currently doing where you place the includes and libs in a common location, perhaps /usr/include and /usr/lib on linux, or ${HOME} on both Windows/Linux, up to you.
Another option is available if you use git. You can include the project inside another using submodules. Then use include_directory(${submodule}) and build and link directly for every project. The advantage of this approach is that dependencies are more localised. One problem with this method is if you recursively do this, you may end up with duplicates of some projects and cmake will complain that two libraries have the same name.