Unifying CMake's FIND_PACKAGE - cmake

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.

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
)

how to used cmake link the pthread and CURSES [duplicate]

I'm running RHEL 5.1 and use gcc.
How I tell cmake to add -pthread to compilation and linking?
#Manuel was part way there. You can add the compiler option as well, like this:
If you have CMake 3.1.0+, this becomes even easier:
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(my_app PRIVATE Threads::Threads)
If you are using CMake 2.8.12+, you can simplify this to:
find_package(Threads REQUIRED)
if(THREADS_HAVE_PTHREAD_ARG)
target_compile_options(my_app PUBLIC "-pthread")
endif()
if(CMAKE_THREAD_LIBS_INIT)
target_link_libraries(my_app "${CMAKE_THREAD_LIBS_INIT}")
endif()
Older CMake versions may require:
find_package(Threads REQUIRED)
if(THREADS_HAVE_PTHREAD_ARG)
set_property(TARGET my_app PROPERTY COMPILE_OPTIONS "-pthread")
set_property(TARGET my_app PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread")
endif()
if(CMAKE_THREAD_LIBS_INIT)
target_link_libraries(my_app "${CMAKE_THREAD_LIBS_INIT}")
endif()
If you want to use one of the first two methods with CMake 3.1+, you will need set(THREADS_PREFER_PTHREAD_FLAG ON) there too.
The following should be clean (using find_package) and work (the find module is called FindThreads):
cmake_minimum_required (VERSION 2.6)
find_package (Threads)
add_executable (myapp main.cpp ...)
target_link_libraries (myapp ${CMAKE_THREAD_LIBS_INIT})
Here is the right anwser:
ADD_EXECUTABLE(your_executable ${source_files})
TARGET_LINK_LIBRARIES( your_executable
pthread
)
equivalent to
-lpthread
target_compile_options solution above is wrong, it won't link the library.
Use:
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -pthread")
OR
target_link_libraries(XXX PUBLIC pthread)
OR
set_target_properties(XXX PROPERTIES LINK_LIBRARIES -pthread)

CMake: How to use LINK_INTERFACE_MULTIPLICITY?

Linking against the Intel MKL static libraries introduces circular dependencies. When I import the libraries,
set(LIBRARIES mkl_intel_lp64 mkl_sequential mkl_core)
foreach(_lib ${LIBRARIES})
add_library(${_lib} UNKNOWN IMPORTED)
set_target_properties(${_lib} PROPERTIES IMPORTED_LOCATION
/opt/intel/mkl/lib/intel64/lib${_lib}.a)
endforeach()
and link to my executable,
target_link_libraries(main PRIVATE ${LIBRARIES})
I get a ton of undefined references to the linear algebra calls. For example,
ztrevc3_gen.f:(.text+0x1af7): undefined reference to `mkl_blas_zdscal'
One way to get around this is to use the appropriate linker flags:
target_link_libraries(main PRIVATE -Wl,--start-group ${LIBRARIES} -Wl,--end-group)
Another option is to do this:
target_link_libraries(main PRIVATE ${LIBRARIES} ${LIBRARIES} ${LIBRARIES})
However, as I was searching for a more elegant solution I came across the LINK_INTERFACE_MULTIPLICITY property. If set this property along with the imported library location,
set(LIBRARIES mkl_intel_lp64 mkl_sequential mkl_core)
foreach(_lib ${LIBRARIES})
add_library(${_lib} UNKNOWN IMPORTED)
set_target_properties(${_lib} PROPERTIES IMPORTED_LOCATION
/opt/intel/mkl/lib/intel64/lib${_lib}.a
LINK_INTERFACE_MULTIPLICITY 3)
endforeach()
I get the same undefined references as before, so appearently this is not working. What is the proper way to use LINK_INTERFACE_MULTIPLICITY and is there a more elegant way to get around circular dependencies?
EDIT
Here is a minimal example that fails, this time with the correct IMPORTED_LINK_INTERFACE_MULTIPLICITY variable.
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(test Fortran)
add_executable(main main.f90)
set(LIBRARIES mkl_intel_lp64 mkl_sequential mkl_core)
foreach(_lib ${LIBRARIES})
add_library(${_lib} UNKNOWN IMPORTED)
set_target_properties(${_lib} PROPERTIES IMPORTED_LOCATION
/opt/intel/mkl/lib/intel64/lib${_lib}.a
IMPORTED_LINK_INTERFACE_MULTIPLICITY 3)
endforeach()
list(APPEND LIBRARIES dl pthread)
target_link_libraries(main ${LIBRARIES})
#target_link_libraries(main ${LIBRARIES} ${LIBRARIES} ${LIBRARIES})
# main.f90
call zpotrf
end program
If you uncomment the last line then the build succeeds. Unfortunately, the MKL in not free (except in some cases) but hopefully somebody can test this. I should note that it fails with some linear algebra calls and not others like dgemm.
Property LINK_INTERFACE_MULTIPLICITY is for "normal" STATIC library. For IMPORTED library IMPORTED_LINK_INTERFACE_MULTIPLICITY property should be used instead:
set_target_properties(${_lib} PROPERTIES
IMPORTED_LOCATION /opt/intel/mkl/lib/intel64/lib${_lib}.a
IMPORTED_LINK_INTERFACE_MULTIPLICITY 3)

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).

Transitively require other imported library's interface from imported library

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.