cmake doesn't support imported libraries? - cmake

When I try to import a library using
add_library(libname SHARED IMPORTED)
set_property(TARGET libname PROPERTY IMPORTED_LOCATION /<foldername>/<sub-foldername>/lib")
The cmake shouts :
CMake Warning (dev) at /CMakeLists.txt:28
(target_link_libraries): Cannot specify link libraries for target
"libname" which is not built by this project.
CMake does not support this but it used to work accidentally and is
being allowed for compatibility.
Policy CMP0016 is not set: target_link_libraries() reports error if
only argument is not a target. Run "cmake --help-policy CMP0016"
for policy details. Use the cmake_policy command to set the policy
and suppress this warning. This warning is for project developers.
Use -Wno-dev to suppress it.
If this is true, what is the other best way to include a library somewhere in my build tree into another project.
I have a library setup and another place has executable which will be using the libraries.
Reading through the cmake documentation, it felt like this would be the best way forward but seems its a broken piece which is just being supported.

Cannot specify link libraries for target "libname" which is not built by this project
When you use target_link_libraries to some target you're specifying how to build it,
but imported library is already build. CMake told you that...
Example of linking imported target to executable:
add_library(boo SHARED IMPORTED)
set_target_properties(boo PROPERTIES IMPORTED_LOCATION "/path/to/boo/library")
add_executable(foo foo.cpp)
target_link_libraries(foo boo)
Note: using imported targets

I was getting the same error as navderm when trying to import the Poco C++ libPocoFoundation.so library into my project, and after trying different solutions unsuccessfully, I managed to find one that worked for me:
cmake_minimum_required(VERSION 3.5)
project(MyProject)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
add_library(PocoLib SHARED IMPORTED GLOBAL)
# It's important to specify the full path to the library you want to import
set_target_properties(PocoLib PROPERTIES IMPORTED_LOCATION "/usr/local/lib/Poco_1.7.2/lib/libPocoFoundation.so")
# create my executable
set(EXEC_SOURCE_FILES main.cpp)
add_executable(MyProject ${EXEC_SOURCE_FILES})
target_link_libraries(MyProject PocoLib)

Related

Create external module import files [duplicate]

I've a strange problem with CMake.
I'm importing Curl into my project, so I write for you a simplified summary of my CMakeLists.txt file.
ADD_LIBRARY (libcurl SHARED IMPORTED)
SET_PROPERTY(TARGET libcurl PROPERTY IMPORTED_LOCATION
../lib/libcurl.lib)
When I run CMake it generates the project files for MS VC++ (also for Linux). Then into the project file I find a wrong reference to curl library (libcurl-NOTFOUND)!
If I change my code into static import:
ADD_LIBRARY (libcurl STATIC IMPORTED)
SET_PROPERTY(TARGET libcurl PROPERTY IMPORTED_LOCATION
../lib/libcurl.lib)
I find the right reference to ../lib/libcurl.lib.
Do you have any idea why this happens?
Thank you very much!
For a shared library, the IMPORTED_LOCATION must point to the DLL, not the import lib. See the documentation. You might also want to set the IMPORTED_IMPLIB property.
BTW, CMake also has a find package for Curl; perhaps you could use that?

Add only headers of an imported module to a library in CMake

In CMake there are imported modules that are used to simply add external modules to local targets. For example if we want to use boost::filesystem library in our project we could have a CMakeLists.txt like this:
project(foo CXX)
find_packge(Boost REQUIRED COMPONENTS filesystem)
add_executable(foo main.cpp)
target_link_libraries(foo Boost::filesystem)
With above configuration CMake will add proper compiler options and include directories among required libraries to building process of the foo.
Now we have to build a library instead of an executable and we don't want to link boost::filesystem libraries to our library. We want only compiler options and include directories to be added to our target. Could we use imported modules concepts here? I mean that if we could use Boost::filesystem syntax for adding those options to our target?
project(foo CXX)
find_packge(Boost REQUIRED COMPONENTS filesystem)
add_library(foo STATIC foo.cpp)
# what should be wrote here to only add headers and configs to foo not the libs?
Turning my comments into an answer
add_library(STATIC) won't link the target_link_libraries() dependencies into itself.
In short, if two static libraries would include e.g. Boost::filesystem and then you link both of those libraries into an executable (where the external symbols get actually resolved) you would get duplicate symbol errors.
So CMake by default, does not add linker options like --whole-archive for gcc or LinkLibraryDependencies for VC.
target_link_libraries(foo Boost::filesystem) should work, it just describes the dependency resolved later when building a executable or shared library.
References
ld linker question: the --whole-archive option
CMake issue #9732: Cmake does not disable Link Libray Dependencies in the project settings

Why use add_library({tgt} IMPORTED) versus target_link_libraries( -l {.so | .a})?

What is the purpose of using the statement:
add_library(<tgt> [SHARED|STATIC] IMPORTED)
From what I have found even if you create an imported library target above you still would need to specify the specific location of the actual .so or .a. This would take at least 3 cmake commands to link to an executable and the compiler still would not automatically search through the common include directories on your OS.
Example:
cmake code to link IMPORTED lib
From the CMake documentation I understand there are really 3 ways to link a library that is not built as a target in a subproject of your overall application/library.
CMake target_link_libraries() documentation
Using a CMake package for one of the shipped package scripts.
Using a linker flag:
target_link_libraries(<tgt> [SHARED|STATIC|...] -lncursesw)
Or using the IMPORTED library method (showcased in code at top).
A major difference when using the second method is that it only takes a single line of code and will search through all of your compiler's predefined include directories on you OS. Could anyone help me understand why the add_library() method is used?
Additional Realated SO Posts:
Include directories for IMPORTED libs
CMake imported library behavior
You should use add_library(<tgt> [SHARED|STATIC] IMPORTED) whenever you need to set properties such as dependencies, compile definitions, compile flags etc for <tgt>, and/or by extension, any targets that are linking against <tgt>.
Let's say you have two static libraries; libfoobar.a and libraboof.a, where libfoobar.a requires libraboof.a. Let's also say that these libraries contain some features that are enabled by -DSOME_FEATURE.
add_library(raboof STATIC IMPORTED)
set_target_properties(raboof PROPERTIES
IMPORTED_LOCATION <path-to-libraboof.a>
INTERFACE_COMPILE_DEFINITIONS "SOME_FEATURE"
)
add_library(foobar STATIC IMPORTED)
set_target_properties(foobar PROPERTIES
IMPORTED_LOCATION <path-to-libfoobar.a>
INTERFACE_LINK_LIBRARIES raboof
)
So when you link against libfoobar.a:
add_executable(my_app main.cpp)
target_link_libraries(my_app foobar)
CMake will make sure to link all dependencies in the correct order and will in this case also append -DSOME_FEATURE to the compile flags when you build my_app. Note that since we added libraboof.a as a dependency to libfoobar.a, -DSOME_FEATURE is added to any target that link against libfoobar.a through the transitive property.
If you don't use add_library(<tgt> <SHARED|STATIC> IMPORTED) in a scenario like this, you would have to manage any dependencies and required build options yourself for each target, which is quite error-prone.
This method is also often used in Config-modules for multi-component libraries to manage dependencies between the components.

Making a CMake library accessible by other CMake packages automatically

I have one project that produces a library:
project (myCoolLibrary)
ADD_LIBRARY(my_cool_library SHARED ${mysources_SRC})
And another project that should be using this library:
find_package (myCoolLibrary REQUIRED)
INCLUDE_DIRECTORIES("${myCoolLibrary_INCLUDE_DIRS}" )
add_executable(myCoolExe ${my_sources_SRC} )
TARGET_LINK_LIBRARIES(myCoolExe ${myCoolLibrary_LIBRARIES} )
Is there a way that I can change the first file so that the second file works automatically? That by running CMake on the first file and then running make on the output, then running CMake on the second file, CMake is able to find the package?
An answer where I just give the address of where the first project is built to the second package is also acceptable.
Taking the code found in a blog post by #daniperez - Use CMake-enabled libraries in your CMake project (III) - I've come up with the following minimal solution:
myCoolLibrary/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolLibrary)
function(my_export_target _target _include_dir)
file(
WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Config.cmake"
"
include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/${_target}Targets.cmake\")
set_property(
TARGET ${_target}
APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES \"${_include_dir}\"
)
"
)
export(TARGETS ${_target} FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Targets.cmake")
# NOTE: The following call can pollute your PC's CMake package registry
# See comments/alternatives below
export(PACKAGE ${_target})
endfunction(my_export_target)
...
add_library(${PROJECT_NAME} SHARED ${mysources_SRC})
my_export_target(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}")
myCoolExe/CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(myCoolExe)
find_package(myCoolLibrary REQUIRED)
...
add_executable(${PROJECT_NAME} ${my_sources_SRC})
target_link_libraries(${PROJECT_NAME} myCoolLibrary)
To make it reusable I have packed everything into my_export_target(). And I'm friend of self-propagating properties like INTERFACE_INCLUDE_DIRECTORIES.
As commented by #ruslo, using export(PACKAGE ...) can pollute your package registry. So alternatively you can:
Write the target configuration files directly to some dedicated place specific for a certain toolchain
See e.g. How to install your custom CMake-Find module and 0003659: FIND_PACKAGE command improvements.
Set CMAKE_MODULE_PATH via the second project's CMake command line (injecting the search path(s) from the outside). If you are building the two projects anyway with a build script, then this is the most direct way to propagate the module search path(s).
Additional References
export()
CMake/Tutorials/Package Registry
Unable to find Eigen3 with CMake
How to instruct CMake to use the build architecture compiler

Building shared libraries with cmake with option -Wl,--no-undefined

I'm trying to create a ndk library using android-cmake.
My target here is a shared library and build fails because of option
-Wl,--no-undefined
As the targets refers symbols from another library.
Is there a way to the missing library to the command
add_library(foo SHARED ${sources})
I was able to build by changing cmake variable
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L<path to library> -lfoo")