Transitively require other imported library's interface from imported library - cmake

CMake 3.5
I have an existing external library, which I have made an IMPORTED library in my CMakeLists.txt:
find_path(
FOO_INCLUDE_DIR NAMES foo.h
PATHS "${FOO_ROOT}"
NO_DEFAULT_PATH
PATH_SUFFIXES include/foo
)
find_library(
FOO_LIBRARY NAMES foo
PATHS "${FOO_ROOT}"
PATH_SUFFIXES lib
)
mark_as_advanced(FOO_INCLUDE_DIR FOO_LIBRARY)
find_package_handle_standard_args(
FOO REQUIRED_VARS
FOO_INCLUDE_DIR
FOO_LIBRARY
)
add_library(Foo::Foo SHARED IMPORTED)
set_property(TARGET Foo::Foo
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
"${FOO_INCLUDE_DIR}" "${BAR_INCLUDE_DIR}"
"${Boost_INCLUDE_DIRS}" "${BAZ_INCLUDE_DIR}"
)
set_property(TARGET Foo::Foo
PROPERTY IMPORTED_LOCATION
"${FOO_LIBRARY}"
)
set_property(TARGET Foo::Foo
PROPERTY INTERFACE_LINK_LIBRARIES
"${Boost_LIBRARIES}" "${BAR_LIBRARY}" "${BAZ_LIBRARIES}"
)
Programs that link with this particular library need thread support. Adding thread support to something is fairly straightforward:
set(THREADS_PREFER_PTHREAD_FLAG on)
include(FindThreads)
...
target_link_libraries(something PUBLIC Threads::Threads)
I would like anything that links against Foo::Foo to automatically include Threads::Threads. But you can't use target_link_libraries() on an IMPORTED library. So how do I transitively require Threads::Threads from Foo::Foo?
I managed to work around this by doing the following, but it depended on me checking to see what properties FindThreads sets on Threads::Threads. Is there a better way?
set_property(TARGET Foo::Foo
PROPERTY INTERFACE_LINK_LIBRARIES
"${Boost_LIBRARIES}" "${BAR_LIBRARY}" "${BAZ_LIBRARIES}"
$<TARGET_PROPERTY:Threads::Threads,INTERFACE_LINK_LIBRARIES>
)
set_property(TARGET Foo::Foo
PROPERTY INTERFACE_COMPILE_OPTIONS
$<TARGET_PROPERTY:Threads::Threads,INTERFACE_COMPILE_OPTIONS>
)

It turns out the answer was quite simple. I simple needed to add Threads::Threads to the INTERFACE_LINK_LIBRARIES property of Foo::Foo. I have no idea why I had determined that would not work the first time around.

Related

Remove compile option which was populated from interface library

I have some existing cmake file where interface library is used for populating of numerous compile options into another targets. In one of my target I want to exclude one of these options and don't touch another ones.
I tried to use get_target_property/set_target_property but unfortunately didn't find proper usage them.
Does anyone know how to remove option (for example /we4800) from test_project?
cmake_minimum_required (VERSION 3.8)
add_library(common_compile_options INTERFACE)
set_property(TARGET common_compile_options PROPERTY INTERFACE_COMPILE_OPTIONS
/we4309
/we4800
)
add_executable (test_project "main.cpp")
target_link_libraries(test_project common_compile_options)
====UPDATE 1====:
I tried also followed workaround:
cmake_minimum_required (VERSION 3.8)
add_library(common_compile_options INTERFACE)
set_property(TARGET common_compile_options PROPERTY INTERFACE_COMPILE_OPTIONS
/we4309
/we4800
)
get_target_property(CACHE_PROPERTY common_compile_options INTERFACE_COMPILE_OPTIONS)
string(REPLACE "/we4800" "" TEMP_PROPERTY "${CACHE_PROPERTY}")
add_executable (project "main.cpp")
target_link_libraries(project common_compile_options)
set_property(TARGET project PROPERTY COMPILE_OPTIONS ${TEMP_PROPERTY})
get_target_property(PROJECT_PROPERTY project COMPILE_OPTIONS)
message("New properties: " ${PROJECT_PROPERTY})
In cmake output I got message as expected:
New properties: /we4309
But /we4800 option is still passed to the compiler :(
I found out the working solution, but the question is, will it work in all cases?
cmake_minimum_required (VERSION 3.8)
add_library(common_compile_options INTERFACE)
set_property(TARGET common_compile_options PROPERTY INTERFACE_COMPILE_OPTIONS
/we4309
/we4800
)
add_executable (project "main.cpp")
target_link_libraries(project common_compile_options)
set_source_files_properties(main.cpp
DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
PROPERTIES COMPILE_OPTIONS /wd4800
)

CMake - How propage multiple Dll copy with TARGET_RUNTIME_DLLS when using INTERFACE library to group that Dll

I have a library INTERFACE which reference multiples IMPORTED library, one for each Dll/lib.
The INTERFACE library is here to group that Dlls/sublibraries in one lib.
When I'm using TARGET_RUNTIME_DLLS to copy all depending Dlls, the Dlls are not copy if declare through an INTERFACE library.
# OpenSSL eay
add_library(openssl_eay SHARED IMPORTED)
set_property(TARGET openssl_eay PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/Libs/OpenSSL/Bin/libeay32.dll")
set_property(TARGET openssl_eay PROPERTY IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/Libs/OpenSSL/Bin/libeay32.lib")
# OpenSSL ssleay
add_library(openssl_ssleay SHARED IMPORTED)
set_property(TARGET openssl_ssleay PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/Libs/OpenSSL/Bin/ssleay32.dll")
set_property(TARGET openssl_ssleay PROPERTY IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/Libs/OpenSSL/lib/Bin/ssleay32.lib")
# OpenSSL
add_library(openssl INTERFACE IMPORTED)
target_link_libraries(openssl INTERFACE openssl_eay openssl_ssleay)
# Foo
add_library(Foo SHARED IMPORTED)
set_property(TARGET Foo PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/Libs/Foo/Bin/Foo.dll")
set_property(TARGET Foo PROPERTY IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/Libs/Foo/Bin/Foo.lib")
add_executable(MyExecutable Main.cpp)
target_link_libraries(MyExecutable PRIVATE openssl)
add_custom_command(TARGET MyExecutable POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:MyExecutable> $<TARGET_FILE_DIR:MyExecutable>
COMMAND_EXPAND_LISTS
)
With that configuration, CMake only copy Foo.dll next to the executable, but not libeay32.dll and ssleay32.dll.
I try to declare add_library(openssl INTERFACE) without IMPORTED, but same result/problem.
How I can modified openssl library declaration to have the dependant imported Dlls copy?

How to instruct CMake to use external pre-built libraries

I'm struggling how to find a way to say to CMake to include external prebuilt libraries... It's driving me insane.
It's understatement that I'm new to CMake. I just want that MinGW linker add two external .lib files to link list... I'm pulling my hair at this point.
This is my CMakeLists.txt file:
cmake_minimum_required(VERSION 3.9)
project(opengltest C CXX)
set(CMAKE_CXX_STANDARD 11)
file(GLOB
TestSRC
"src/*.h"
"src/*.cpp"
"src/*.c"
)
add_executable(opengltest ${TestSRC})
include_directories(
3rdparty/glew/include
3rdparty/glfw/include
)
link_directories(
${PROJECT_SOURCE_DIR}/3rdparty/glew/lib/Release/x64/
${PROJECT_SOURCE_DIR}/3rdparty/glfw/build/src/Debug/
)
target_link_libraries(${PROJECT_NAME} glew32s glfw3)
Linker says it cannot find glew32s and glfw3.
EDIT: I think I've found the solution:
...
add_library(glew32s STATIC IMPORTED)
set_property(TARGET glew32s PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/glew32s.lib)
add_library(glfw3 STATIC IMPORTED)
set_property(TARGET glfw3 PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/glfw3.lib)
target_link_libraries(${PROJECT_NAME} glew32s glfw3)
I've moved all my .lib files to one folder and used add_library to include them.
...
add_library(glew32s STATIC IMPORTED)
set_property(TARGET glew32s PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/glew32s.lib)
add_library(glfw3 STATIC IMPORTED)
set_property(TARGET glfw3 PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/glfw3.lib)
target_link_libraries(${PROJECT_NAME} glew32s glfw3)
After that linker was able to find lib files.

CMake imported library rpath

I have an IMPORTED SHARED library for and I'm linking with it via target_link_libraries (the library has IMPORTED_LOCATION set).
But then after installation in ldd output I see smth like:
path/on-dev-machine/to/libxxx.so => not found
instead of just
libxxx.so => path/on-testing-machine/to/libxxx.so
Why is that / how do I fix it? I'm adding lib paths to /etc/ld.so.conf.d
Sample code:
include(GNUInstallDirs)
function(add_and_install_lib lib_name location external_dep)
if(${location} MATCHES ".*\\.so")
add_library(${lib_name} SHARED IMPORTED) # MODULE treated as shared
else()
add_library(${lib_name} STATIC IMPORTED)
endif()
set_property(TARGET ${lib_name} PROPERTY IMPORTED_LOCATION ${location})
add_dependencies(${lib_name} ${external_dep})
endfunction()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}") # this doesn't seem to help
If anyone cares, it was the IMPORTED_NO_SONAME property (absence of it set to TRUE) of each imported lib that forced the full path to be taken.
Also CMAKE_SKIP_RPATH and CMAKE_SKIP_INSTALL_RPATH are useful in my opinion to make sure you have clean runtime paths (not straightly related to the question but still).

Unifying CMake's FIND_PACKAGE

Do you know a trick to get a unified output terminology when getting include and library paths from CMake's FIND_PACKAGE?
Sometimes it's FOO_INCLUDE. Sometimes it's FOO_INCLUDE_PATH. Etc.
For example, I would like to find a way to ensure that FOO_INCLUDE and FOO_LIB be always defined when FOO_FOUND is set to TRUE after a call to FIND_PACKAGE.
Turning my comment into an answer
To avoid the need to know the include path, library, etc. dependencies and their variable notations modern find_package's implementation do provide IMPORTED targets like Foo::Foo. But this is - as #Tsyvarev has commented - far from unified through all of CMake's find modules.
So generalizing the CMake's Sample Find Module implementation you could unify your find_package() calls with an overwritten find_package() macro version like the following:
cmake_minimum_required(VERSION 3.2)
project(UnifiedFindPackage)
macro(unify_vars _result)
set(${_result} "")
foreach(_i IN ITEMS ${ARGN})
if (${_i})
list(APPEND ${_result} "${${_i}}")
endif()
endforeach()
endmacro()
macro(find_package _name)
_find_package(${_name} ${ARGN})
if (${_name}_FOUND AND NOT TARGET ${_name}::${_name})
add_library(${_name}::${_name} STATIC IMPORTED GLOBAL)
unify_vars(_var ${_name}_LIBRARY ${_name}_LIB)
if (_var)
set_target_properties(${_name}::${_name} PROPERTIES IMPORTED_LOCATION "${_var}")
endif()
if (${_name}_LIBRARY_RELEASE)
set_property(TARGET ${_name}::${_name} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(${_name}::${_name} PROPERTIES IMPORTED_LOCATION_RELEASE "${${_name}_LIBRARY_RELEASE}")
endif()
if (${_name}_LIBRARY_DEBUG)
set_property(TARGET ${_name}::${_name} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(${_name}::${_name} PROPERTIES IMPORTED_LOCATION_DEBUG "${${_name}_LIBRARY_DEBUG}")
endif()
if (${_name}_LIBRARIES)
set_property(TARGET ${_name}::${_name} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${${_name}_LIBRARIES}")
endif()
unify_vars(_var ${_name}_INCLUDE_DIRS ${_name}_INCLUDE_PATH)
if (_var)
set_property(TARGET ${_name}::${_name} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${_var}")
endif()
unify_vars(_var ${_name}_COMPILE_FLAGS ${_name}_DEFINITIONS)
if (_var)
set_property(TARGET ${_name}::${_name} APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${_var}")
endif()
endif()
endmacro()
find_package(MPI REQUIRED)
add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} MPI::MPI)
This should only demonstrate a possible unification and could be extended on a need-by basis.
Edit: Turning this into an community wiki answer. Please feel free to contribute.
The code was tested in this example with MPI find module results.