IMPORTED library that sets linker search directories only - c++

The project I am working on has numerous external dependencies that I am now converting to proper IMPORTED targets with their IMPORTED_LOCATION or IMPORTED_IMPLIB set, along with their INTERFACE_INCLUDE_DIRECTORIES. This is much cleaner than just adding all directories to each target globally as it was before.
I'm running into a problem that there is now a library that defines the library that needs to be linked in the source file using a MSVC-specific #pragma. I tried adding an MODULE IMPORTED library, but that results in NAME-NOTFOUND.obj being linked, which is not what I want and not what the documentation seems to imply.
I also tried CMake 3.x's INTERFACE IMPORTED library type, which results in the #pragma-defined library being linked, but not found, because I found no way to specify the linker search directory for that target. I would prefer a solution that works for CMake 2.8.12.1.
Is there a way to solve this without naming the exact library file in the CMake script?

Related

Archiving static dependencies in modern CMake project

I need to package a C++ library that links to some other static libraries, and I'd like to be able to ship the compiled files alone without the need to ship the transitive dependencies as well. To this effect, I'm following this guide regarding modern CMake techniques, and I've specified all the needed dependencies as PRIVATE, as they are not used in my library's exposed API.
The issue is that it seems that, despite having specified the dependencies as PRIVATE, the linker still does not include them in the output library, so if I try to link my library to an executable, the linker will complain about missing symbols (at least using MSVC). Is there a way to solve this?
I've already taken a look at this but I'm not sure how to integrate it in the existing INSTALL targets
Assuming that you are creating a static library:
You receive unresolved symbols because dependencies in static libraries are not resolved during their creation. Only when you link the static lib to an executable or shared library, the linker actually tries to resolve the required symbols (and will fail in your case).
So you need to combine your static libraries to a single one (as you already found out).
You should follow the approach of combining add_custom_command and add_custom_target that is outlined in the answers you linked to (https://stackoverflow.com/a/32888999/1228449).
Then use INSTALL( FILES ....) to add the combined library to your install commands, e.g.:
include(GNUInstallDirs)
INSTALL( FILES ${LIBNAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} )
EDIT:
Alternatively, create a shared library.
ADD_LIBRARY( mylibrary SHARED ...)
Then the required symbols of the privately used static libraries are resolved at link time of your library. Furthermore, only the symbols that your library actually uses, are integrated into your library (whereas the static library will contain everything).

Are exported (installed) cmake targets distributable?

I'm working on a project with a lot of external dependencies, which are included in the project tree. I would like to pre-build all of the dependencies and share the importable targets within the project tree.
I was planning to use cmake install with a CMAKE_INSTALL_PREFIX in the source tree, and use CMAKE_PREFIX_PATH to reference it for find_package. However, I'm beginning to wonder how maintainable this strategy is going to be? For example here's something I noticed in one of the installed cmake scripts:
${CMAKE_PREFIX_PATH}/lib/cmake/glfw3/glfw3Targets.cmake:
set_target_properties(glfw PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
INTERFACE_LINK_LIBRARIES "/usr/lib/x86_64-linux-gnu/librt.so;/usr/lib/x86_64-linux-gnu/libm.so;dl;/usr/lib/x86_64-linux-gnu/libX11.so;-lpthread"
)
It seems really suspicious to me that all of those link libraries are fully resolved to paths on the host machine.
I guess the question is: Are cmake installs to a prefix meant to be distributable and this is just a bad example, or are they meant to be tied to the machine you "install" them on? Ie. Is prefix really meant to just relocate where on the system things are supposed to be "installed", and my hope to use it as a shared package manager likely to be problematic?
Yes, EXPORT'ed CMake targets can be "distributable", but the project should follow some principles for achieve that.
If you link with a (external) library, but do not want export file to contain absolute path to it, then do not pass absolute path directly to target_link_libraries.
In case a linked library is shipped with a compiler (e.g. m or rt), things are simple: just pass the library's name to the target_link_libraries.
In case a linked library YYY comes from other package and is detected by find_package(YYY), this implies several things:
Script FindYYY.cmake or YYYConfig.cmake should return IMPORTED target. If this is not true, you may try to wrap its results into IMPORTED target.
target_link_libraries should use this IMPORTED target.
Script XXXConfig.cmake, shipped with your project, should use find_dependency(YYY) for discover a library on a user machine.
For find_dependency(YYY) work, a user machine should have FindYYY.cmake or YYYConfig.cmake script. Alternatively, you may ship FindYYY.cmake with your project, and adjust CMAKE_MODULE_PATH variable before find_dependency() call (in your XXXConfig.cmake).

how to add library source in cmake

i can't seem to find in google/stackoverflow how do i add a library to my project using the library sources so that in make time it will compile the library and then my project and then link between them.
the library i'm trying to add is curlpp
as of now I've tried the following:
add_library(curlpp STATIC IMPORTED ./curlpp)
target_link_libraries(myExec curlpp)
link_directories(./curlpp/src/)
include_directories(./curlpp/includes)
but it has no effect
You first need to add the subdirectory to actually build the library, then you can add the actual library for linking.
If the library is not a CMake project you might need to add a simple CmakeLists.txt file in the library, that executes the actual configuration and building.
You might also use custom commands from the top-level CMakeLists.txt file to configure/build the library.
Basing off on Some programmer dude's response the following commands allowed me to statically link with curlpp:
add_subdirectory(./vendor/curlpp)
set_property(TARGET curlpp PROPERTY IMPORTED_LOCATION ./vendor/curlpp/libcurlpp.a)
target_link_libraries(my_target curlpp)

"No rule to make target" error in cmake when linking to shared library

In Ubuntu, I have downloaded a third-party shared library, mylibrary.so, which I have placed in the directory /home/karnivaurus/Libraries. I have also placed the associated header file, myheader.h, in the directory /home/karnivaurus/Headers. I now want to link to this library in my C++ code, using CMake. Here is my CMakeLists.txt file:
cmake_minimum_required(VERSION 2.0.0)
project(DemoProject)
include_directories(/home/karnivaurus/Headers)
add_executable(demo demo.cpp)
target_link_libraries(demo /home/karnivaurus/Libraries/mylibrary)
However, this gives me the error message:
:-1: error: No rule to make target `/home/karnivaurus/Libraries/mylibrary', needed by `demo'. Stop.
What's going on?
While the other answer posted here is valid, it is out-dated. CMake now provides better solutions for using a pre-built external library in your code. In fact, CMake itself even discourages the use of link_directories() in its documentation.
The target_link_libraries() command takes very specific syntax for linking to an external library. A more modern solution is to create an IMPORTED CMake target for your external library:
add_library(MyExternalLib SHARED IMPORTED)
# Provide the full path to the library, so CMake knows where to find it.
set_target_properties(MyExternalLib PROPERTIES IMPORTED_LOCATION /home/karnivaurus/Libraries/mylibrary.so)
You can then use this imported CMake target later on in your code, and link it to your other targets:
target_link_libraries(demo PRIVATE MyExternalLib)
For other ways to use an external third-party library in your CMake code, see the responses here.
You may use a full path to the static library. To link w/ dynamic one, better to use link_directories() like this:
cmake_minimum_required(VERSION 2.0.0)
project(DemoProject)
include_directories(/home/karnivaurus/Headers)
link_directories(/home/karnivaurus/Libraries)
add_executable(demo demo.cpp)
target_link_libraries(demo mylibrary)
and make sure mylibrary has prefix lib and suffix .so in file name (i.e. full name is /home/karnivaurus/Libraries/libmylibrary.so).
To make you project more flexible, you'd better to write a finder module and avoid hardcode paths like /home/karnivaurus/*

Including my own shared library with cmake

I have the following code in my CMakeLists.txt for finding my shared library libsieve.so
set(CPPLIB_DIR "${CMAKE_SOURCE_DIR}/../core/build")
find_library(CPPLIB_SIEVE_LIBRARY NAMES libsieve PATHS CPPLIB_DIR)
But it fails and won't find my library. I have the following directory structure:
core
build: libsieve.so
project: CMakeLists.txt
What am I doing wrong?
I don't know why cmake doesn't find the needed library but I can suggest a way to make it happen with the help cmake-gui: if the first run of "configure" fails to find the library you can point it to the needed library manually (set the full absolute path). Most of the time such solution works for me.
Also if the library was built with one tool chain (say, Intel C++) and you project is being built with another tool chain (say, clang) the failure to find the library may be due to binary incompatibility between the project and the library.
Upd. The original problem was referencing CPPLIB_DIR. It should have been:
find_library(CPPLIB_SIEVE_LIBRARY NAMES sieve PATHS ${CPPLIB_DIR})
Cmake find_library expect you to provide the library name or the library file name.
You mixed the two by adding a "lib" prefix to your library name. So you should try to replace libsieve by either sieve or libsieve.so.