> Thanks for working on this. Please read through this mailing list
> thread:
>
> IMPORTED targets for some Find modules
> https://cmake.org/pipermail/cmake-developers/2014-June/010694.html
Done.
> On 11/16/2015 09:26 AM, Florent Castelli wrote:
>> But one thereâs one thing that comes to mind. Some compiled libraries
>> have dependencies on other compiled libraries.
>> Donât you think it would make sense to teach FindBoost about those
>> so we link everything properly?
>>
>> And also maybe add the native dependencies for some modules that
>> have one like maybe pthread or openssl.
>> Though, this might be hard to get right in a cross platform way.
>
> Please look at adding these. The imported targets should come
> with all usage requirements as if they were provided by upstream
> Boost. It may be tricky to get right on all platforms but if we
> do not do it then application code will have to do it instead.
I have made a first attempt at this. I suspect it will need some
refinement, but hopefully useful as a starting point. Please see the
attached patch. I'd certainly be very appreciative of any comments.
I considered whether we would need to hardcode all the dependencies, since
some are known. But since they can change from release to release, and we
don't know the complete set, I opted to do this automatically to ensure
it's correct (for some definition of correct) for any arbitrary Boost
release.
- This will iteratively discover all the potential dependencies for each
component via the header defines which would trigger autolinking on
Windows. This is very aggressive and not all the discovered libraries may
be required under all circumstances, if e.g. a particular set of #defines
and headers must be used to pull in another component dependency, we will
pull it in anyway. However, it is almost always going to link
successfully as a result.
- Any missing dependencies are automatically added to the component list
and as imported targets.
This makes linking correctly much simpler and much more transparent, as
demonstrated by the added testcase.
Problems and missing features:
- Scanning all the headers line-by-line is slow, and this is noticeable;
if I have read a file into a variable and converted it to a list, if there
was a way of filtering the list in a single pass, that would be a big
improvement. We basically just want the #includes and library names, but
don't want to run two regexes on every line.
- I've had to special-case boost_bzip2 and boost_zlib since they don't
work under all (most?) circumstances.
- I've made boost_thread link with Thread::Thread automatically. I'm not
a heavy user of all Boost library components, so other dependencies will
also require adding.
- Header-only components with implicit dependencies on components with
libraries are not currently handled; these could be added as imported
targets as a future improvement.
- You can now disable autolinking and enable shared libraries with
imported targets. (Personally, I've never had autolinking work ever--it
can never find the libs! Once the dependency discovery is fully
functional, we could potentially always disable it when using the imported
targets to give consistent linking behaviour on all platforms.)
In the mailing list thread you pointed to, there was some question over
FindBoost being compatible with the future modularised Boost libraries and
with Boost using CMake in the future. These are reasonable concerns, but
personally I'd rather have something that works now with current and
previous Boost releases, since progress upstream is somewhat unpredictable
and leaves us in limbo waiting on them otherwise. So long as the
modularised Boost cmake config files are compatible with the variables and
targets defined here, and to be backward compatible they would have to be,
won't they be automatically used instead of FindBoost when installed?
Unless I'm missing something, upstream could make the modularisation
completely transparent if they care to be backward compatible with what is
already in wide use.
Regards,
Roger
>From 0a15cd57d0d2924f77372b65985f5b5c0ee477c1 Mon Sep 17 00:00:00 2001
From: Roger Leigh <rle...@dundee.ac.uk>
Date: Mon, 16 Nov 2015 13:21:33 +0000
Subject: [PATCH] FindBoost: Add imported targets
---
Modules/FindBoost.cmake | 216 ++++++++++++++++++++++++++++++++++++
Tests/CMakeLists.txt | 6 +-
Tests/FindBoost/CMakeLists.txt | 10 ++
Tests/FindBoost/Test/CMakeLists.txt | 18 +++
Tests/FindBoost/Test/main.cxx | 26 +++++
5 files changed, 275 insertions(+), 1 deletion(-)
create mode 100644 Tests/FindBoost/CMakeLists.txt
create mode 100644 Tests/FindBoost/Test/CMakeLists.txt
create mode 100644 Tests/FindBoost/Test/main.cxx
diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake
index 33e6a49..d688734 100644
--- a/Modules/FindBoost.cmake
+++ b/Modules/FindBoost.cmake
@@ -54,6 +54,33 @@
# Boost_<C>_LIBRARY_DEBUG - Component <C> library debug variant
# Boost_<C>_LIBRARY_RELEASE - Component <C> library release variant
#
+# The following :prop_tgt:`IMPORTED` targets are also defined::
+#
+# Boost::boost - Target for header-only dependencies
+# (Boost include directory)
+# Boost::<C> - Target for specific component dependency
+# (shared or static library); <C> is lower-
+# case
+# Boost::diagnostic_definitions - interface target to enable diagnostic
+# information about Boost's automatic linking
+# during compilation (adds BOOST_LIB_DIAGNOSTIC)
+# Boost::disable_autolinking - interface target to disable automatic
+# linking with MSVC (adds BOOST_ALL_NO_LIB)
+# Boost::dynamic_linking - interface target to enable dynamic linking
+# linking with MSVC (adds BOOST_ALL_DYN_LINK)
+#
+# Implicit dependencies such as Boost::filesystem requiring
+# Boost::system will be automatically detected and satisfied, even
+# if system is not specified when using find_package and if
+# Boost::system is not added to target_link_libraries. If using
+# Boost::thread, then Thread::Thread will also be added automatically.
+#
+# It is important to note that the imported targets behave differently
+# than variables created by this module: multiple calls to
+# find_package(Boost) in the same directory or sub-directories with
+# different options (e.g. static or shared) will not override the
+# values of the targets created by the first call.
+#
# Users may set these hints or results as cache entries. Projects
# should not read these entries directly but instead use the above
# result variables. Note that some hint names start in upper-case
@@ -142,6 +169,14 @@
# add_executable(foo foo.cc)
# endif()
#
+# Example to find Boost libraries and use imported targets::
+#
+# find_package(Boost 1.56 REQUIRED COMPONENTS
+# date_time filesystem iostreams)
+# add_executable(foo foo.cc)
+# target_link_libraries(foo Boost::date_time Boost::filesystem
+# Boost::iostreams)
+#
# Example to find Boost headers and some *static* libraries::
#
# set(Boost_USE_STATIC_LIBS ON) # only find static libs
@@ -472,6 +507,96 @@ function(_Boost_GUESS_COMPILER_PREFIX _ret)
endfunction()
#
+# Determine header dependencies on libraries using the embedded dependency information.
+#
+# component - the component to check (uses all headers from boost/${component})
+# includedir - the path to the Boost headers
+# _ret - list of library dependencies
+#
+function(_Boost_COMPONENT_DEPENDENCIES component includedir _ret)
+ # _boost_unprocessed_headers - list of headers requiring parsing
+ # _boost_processed_headers - headers already parsed (or currently being parsed)
+ # _boost_new_headers - new headers discovered for future processing
+ #
+ # Start by finding all headers for the component; header dependencies will be solved by future passes
+ file(GLOB_RECURSE _boost_unprocessed_headers RELATIVE "${includedir}" "${includedir}/boost/${component}/*")
+
+ while(_boost_unprocessed_headers)
+ list(APPEND _boost_processed_headers ${_boost_unprocessed_headers})
+ foreach(header ${_boost_unprocessed_headers})
+ if(EXISTS "${includedir}/${header}")
+ file(READ "${includedir}/${header}" headercontent)
+ string(REGEX REPLACE ";" "\\\\;" headercontent "${headercontent}")
+ string(REGEX REPLACE "\n" ";" headercontent "${headercontent}")
+ foreach(line ${headercontent})
+ if(line MATCHES "^#[ \t]*include[ \t]*<boost/[^>][^>]*>")
+ string(REGEX REPLACE "^#[ \t]*include[ \t]*<(boost/[^>][^>]*)>.*" "\\1" _boost_header_match "${line}")
+ list(FIND _boost_processed_headers "${_boost_header_match}" _boost_header_processed)
+ list(FIND _boost_new_headers "${_boost_header_match}" _boost_header_new)
+ if (_boost_header_processed EQUAL -1 AND _boost_header_new EQUAL -1)
+ list(APPEND _boost_new_headers ${_boost_header_match})
+ endif()
+ endif()
+ if(line MATCHES "^#[ \t]*define[ \t][ \t]*BOOST_LIB_NAME[ \t][ \t]*boost_")
+ string(REGEX REPLACE "^#[ \t]*define[ \t][ \t]*BOOST_LIB_NAME[ \t][ \t]*boost_([^ \t][^ \t]*).*" "\\1" _boost_component_match "${line}")
+ list(FIND _boost_DEPS "${_boost_component_match}" _boost_dep_found)
+ # Don't add self as dependency.
+ if (_boost_dep_found EQUAL -1 AND NOT "${_boost_component_match}" STREQUAL "${component}")
+ list(APPEND _boost_DEPS "${_boost_component_match}")
+ endif()
+ endif()
+ endforeach()
+ endif()
+ endforeach()
+ set(_boost_unprocessed_headers ${_boost_new_headers})
+ unset(_boost_new_headers)
+ endwhile()
+ set(${_ret} ${_boost_DEPS} PARENT_SCOPE)
+ string(TOUPPER ${component} uppercomponent)
+ set(_Boost_${uppercomponent}_DEPENDENCIES ${_boost_DEPS} PARENT_SCOPE)
+endfunction()
+
+#
+# Determine if any missing dependencies require adding to the component list.
+#
+# componentvar - the component list variable name
+# includedir - the path to the Boost headers
+#
+function(_Boost_MISSING_DEPENDENCIES componentvar includedir)
+ # _boost_unprocessed_components - list of components requiring processing
+ # _boost_processed_components - components already processed (or currently being processed)
+ # _boost_new_components - new components discovered for future processing
+ #
+ list(APPEND _boost_unprocessed_components ${${componentvar}})
+
+ while(_boost_unprocessed_components)
+ list(APPEND _boost_processed_components ${_boost_unprocessed_components})
+ foreach(component ${_boost_unprocessed_components})
+ _Boost_COMPONENT_DEPENDENCIES("${component}" "${includedir}" _boost_component_deps)
+ string(TOUPPER ${component} uppercomponent)
+ set(_Boost_${uppercomponent}_DEPENDENCIES ${_Boost_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE)
+ foreach(componentdep ${_boost_component_deps})
+ if(componentdep STREQUAL "bzip2" OR componentdep STREQUAL "zlib")
+ # These components may or may not be required; not possible
+ # to tell without knowing where and when BOOST_BZIP2_BINARY
+ # and BOOST_ZLIB_BINARY are defined. If building against an
+ # external zlib or bzip2, this is undesirable.
+ continue()
+ endif()
+ list(FIND _boost_processed_components "${componentdep}" _boost_component_found)
+ list(FIND _boost_new_components "${componentdep}" _boost_component_new)
+ if (_boost_component_found EQUAL -1 AND _boost_component_new EQUAL -1)
+ list(APPEND _boost_new_components ${componentdep})
+ endif()
+ endforeach()
+ endforeach()
+ set(_boost_unprocessed_components ${_boost_new_components})
+ unset(_boost_new_components)
+ endwhile()
+ set(${componentvar} ${_boost_processed_components} PARENT_SCOPE)
+endfunction()
+
+#
# End functions/macros
#
#-------------------------------------------------------------------------------
@@ -562,6 +687,16 @@ if(Boost_DEBUG)
"Boost_NO_SYSTEM_PATHS = ${Boost_NO_SYSTEM_PATHS}")
endif()
+# Supply Boost_LIB_DIAGNOSTIC_DEFINITIONS as a convenience target. It
+# will only contain any interface definitions on WIN32, but is created
+# on all platforms to keep end user code free from platform dependent
+# code. Also provide convenience targets to disable autolinking and
+# enable dynamic linking.
+if(NOT TARGET Boost::diagnostic_definitions)
+ add_library(Boost::diagnostic_definitions INTERFACE IMPORTED)
+ add_library(Boost::disable_autolinking INTERFACE IMPORTED)
+ add_library(Boost::dynamic_linking INTERFACE IMPORTED)
+endif()
if(WIN32)
# In windows, automatic linking is performed, so you do not have
# to specify the libraries. If you are linking to a dynamic
@@ -581,6 +716,12 @@ if(WIN32)
# code to emit a #pragma message each time a library is selected
# for linking.
set(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC")
+ set_target_properties(Boost::diagnostic_definitions PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "BOOST_LIB_DIAGNOSTIC")
+ set_target_properties(Boost::disable_autolinking PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB")
+ set_target_properties(Boost::dynamic_linking PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_DYN_LINK")
endif()
_Boost_CHECK_SPELLING(Boost_ROOT)
@@ -979,6 +1120,17 @@ if(Boost_VERSION AND Boost_FIND_COMPONENTS)
endif()
endif()
+# Additional components may be required via component dependencies.
+# Add any missing components to the list.
+_Boost_MISSING_DEPENDENCIES(Boost_FIND_COMPONENTS "${Boost_INCLUDE_DIR}")
+
+# If thread is required, get the thread libs as a dependency
+list(FIND Boost_FIND_COMPONENTS thread _Boost_THREAD_DEPENDENCY_LIBS)
+if(NOT _Boost_THREAD_DEPENDENCY_LIBS EQUAL -1)
+ include(CMakeFindDependencyMacro)
+ find_dependency(Threads)
+endif()
+
# If the user changed any of our control inputs flush previous results.
if(_Boost_CHANGE_LIBDIR OR _Boost_CHANGE_LIBNAME)
foreach(COMPONENT ${_Boost_COMPONENTS_SEARCHED})
@@ -1222,6 +1374,70 @@ else()
endif()
# ------------------------------------------------------------------------
+# Add imported targets
+# ------------------------------------------------------------------------
+
+if(Boost_FOUND)
+ # For header-only libraries
+ if(NOT TARGET Boost::boost)
+ add_library(Boost::boost INTERFACE IMPORTED)
+ if(Boost_INCLUDE_DIRS)
+ set_target_properties(Boost::boost PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}")
+ endif()
+ endif()
+
+ foreach(COMPONENT ${Boost_FIND_COMPONENTS})
+ if(NOT TARGET Boost::${COMPONENT})
+ string(TOUPPER ${COMPONENT} UPPERCOMPONENT)
+ if(Boost_${UPPERCOMPONENT}_FOUND)
+ if(Boost_USE_STATIC_LIBS)
+ add_library(Boost::${COMPONENT} STATIC IMPORTED)
+ else()
+ # Even if Boost_USE_STATIC_LIBS is OFF, we might have static
+ # libraries as a result.
+ add_library(Boost::${COMPONENT} UNKNOWN IMPORTED)
+ endif()
+ if(Boost_INCLUDE_DIRS)
+ set_target_properties(Boost::${COMPONENT} PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}")
+ endif()
+ if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY}")
+ set_target_properties(Boost::${COMPONENT} PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
+ IMPORTED_LOCATION "${Boost_${UPPERCOMPONENT}_LIBRARY}")
+ endif()
+ if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG}")
+ set_property(TARGET Boost::${COMPONENT} APPEND PROPERTY
+ IMPORTED_CONFIGURATIONS DEBUG)
+ set_target_properties(Boost::${COMPONENT} PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX"
+ IMPORTED_LOCATION_DEBUG "${Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG}")
+ endif()
+ if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE}")
+ set_property(TARGET Boost::${COMPONENT} APPEND PROPERTY
+ IMPORTED_CONFIGURATIONS RELEASE)
+ set_target_properties(Boost::${COMPONENT} PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX"
+ IMPORTED_LOCATION_RELEASE "${Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE}")
+ endif()
+ if(_Boost_${UPPERCOMPONENT}_DEPENDENCIES)
+ unset(_Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES)
+ foreach(dep ${_Boost_${UPPERCOMPONENT}_DEPENDENCIES})
+ list(APPEND _Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES Boost::${dep})
+ endforeach()
+ if(COMPONENT STREQUAL "thread")
+ list(APPEND _Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES Threads::Threads)
+ endif()
+ set_target_properties(Boost::${COMPONENT} PROPERTIES
+ INTERFACE_LINK_LIBRARIES "${_Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES}")
+ endif()
+ endif()
+ endif()
+ endforeach()
+endif()
+
+# ------------------------------------------------------------------------
# Notification to end user about what was found
# ------------------------------------------------------------------------
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index f381758..b31d115 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1356,7 +1356,11 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
endif()
endif()
- if(CMake_TEST_FindGSL)
+ if(CMake_TEST_FindBoost)
+ add_subdirectory(FindBoost)
+ endif()
+
+if(CMake_TEST_FindGSL)
add_subdirectory(FindGSL)
endif()
if(CMake_TEST_FindJsonCpp)
diff --git a/Tests/FindBoost/CMakeLists.txt b/Tests/FindBoost/CMakeLists.txt
new file mode 100644
index 0000000..259ee26
--- /dev/null
+++ b/Tests/FindBoost/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_test(NAME FindBoost.Test COMMAND
+ ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/FindBoost/Test"
+ "${CMake_BINARY_DIR}/Tests/FindBoost/Test"
+ ${build_generator_args}
+ --build-project TestFindBoost
+ --build-options ${build_options}
+ --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+ )
diff --git a/Tests/FindBoost/Test/CMakeLists.txt b/Tests/FindBoost/Test/CMakeLists.txt
new file mode 100644
index 0000000..ce50fc7
--- /dev/null
+++ b/Tests/FindBoost/Test/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.1)
+project(TestFindBoost CXX)
+include(CTest)
+
+find_package(Boost REQUIRED COMPONENTS filesystem thread)
+
+add_executable(test_boost_tgt main.cxx)
+target_link_libraries(test_boost_tgt
+ Boost::dynamic_linking
+ Boost::disable_autolinking
+ Boost::filesystem
+ Boost::thread)
+add_test(NAME test_boost_tgt COMMAND test_boost_tgt)
+
+add_executable(test_boost_var main.cxx)
+target_include_directories(test_boost_var PRIVATE ${Boost_INCLUDE_DIRS})
+target_link_libraries(test_boost_var PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${Boost_THREAD_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+add_test(NAME test_boost_var COMMAND test_boost_var)
diff --git a/Tests/FindBoost/Test/main.cxx b/Tests/FindBoost/Test/main.cxx
new file mode 100644
index 0000000..0f44f30
--- /dev/null
+++ b/Tests/FindBoost/Test/main.cxx
@@ -0,0 +1,26 @@
+#include <boost/filesystem.hpp>
+#include <boost/thread.hpp>
+
+namespace
+{
+
+ boost::mutex m1;
+ boost::recursive_mutex m2;
+
+ void
+ threadmain()
+ {
+ boost::lock_guard<boost::mutex> lock1(m1);
+ boost::lock_guard<boost::recursive_mutex> lock2(m2);
+
+ boost::filesystem::path p(boost::filesystem::current_path());
+ }
+
+}
+
+int main() {
+ boost::thread foo(threadmain);
+ foo.join();
+
+ return 0;
+}
--
2.5.0
--
Powered by www.kitware.com
Please keep messages on-topic and check the CMake FAQ at:
http://www.cmake.org/Wiki/CMake_FAQ
Kitware offers various services to support the CMake community. For more
information on each offering, please visit:
CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html
Visit other Kitware open-source projects at
http://www.kitware.com/opensource/opensource.html
Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake-developers