# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. # Scan the Boost headers and determine the library dependencies. Note # that this script only scans one Boost version at once; invoke once # for each Boost release. Note that this does require the headers for # a given component to match the library name, since this computes # inter-library dependencies. Library components for which this # assumption does not hold true and which have dependencies on other # Boost libraries will require special-casing. It also doesn't handle # private dependencies not described in the headers, for static # library dependencies--this is also a limitation of auto-linking, and # I'm unaware of any specific instances where this would be # problematic. # # Invoke in script mode, defining these variables: # BOOST_DIR - the root of the boost includes # # The script will process each directory under the root as a # "component". For each component, all the headers will be scanned to # determine the components it depends upon by following all the # possible includes from this component. This is to match the # behavior of autolinking. # Written by Roger Leigh # 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_libs - list of library dependencies # function(_Boost_FIND_COMPONENT_DEPENDENCIES component includedir _ret_libs) # _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 set(library_component FALSE) # Start by finding all headers for the component; header # dependencies via #include will be solved by future passes file(GLOB_RECURSE _boost_mpi_python_headers RELATIVE "${includedir}" "${includedir}/boost/mpi/python/*") list(INSERT _boost_mpi_python_headers 0 "boost/mpi/python.hpp") file(GLOB_RECURSE _boost_python_numpy_headers RELATIVE "${includedir}" "${includedir}/boost/python/numpy/*") list(INSERT _boost_python_numpy_headers 0 "boost/python/numpy.hpp") # Special-case since it is part of mpi; look only in boost/mpi/python* if(component STREQUAL "mpi_python") set(_boost_DEPS "python\${component_python_version}") set(library_component TRUE) set(_boost_unprocessed_headers ${_boost_mpi_python_headers}) # Special-case since it is part of python; look only in boost/python/numpy* elseif(component STREQUAL "numpy") set(_boost_DEPS "python\${component_python_version}") set(library_component TRUE) set(_boost_unprocessed_headers ${_boost_python_numpy_headers}) # Special-case since it is a serialization variant; look in boost/serialization elseif(component STREQUAL "wserialization") set(library_component TRUE) file(GLOB_RECURSE _boost_unprocessed_headers RELATIVE "${includedir}" "${includedir}/boost/serialization/*") list(INSERT _boost_unprocessed_headers 0 "boost/serialization.hpp") # Not really a library in its own right, but treat it as one elseif(component STREQUAL "math") set(library_component TRUE) file(GLOB_RECURSE _boost_unprocessed_headers RELATIVE "${includedir}" "${includedir}/boost/math/*") list(INSERT _boost_unprocessed_headers 0 "boost/math.hpp") # Single test header elseif(component STREQUAL "unit_test_framework") set(library_component TRUE) set(_boost_unprocessed_headers "${BOOST_DIR}/test/unit_test.hpp") # Single test header elseif(component STREQUAL "prg_exec_monitor") set(library_component TRUE) set(_boost_unprocessed_headers "${BOOST_DIR}/test/prg_exec_monitor.hpp") # Single test header elseif(component STREQUAL "test_exec_monitor") set(library_component TRUE) set(_boost_unprocessed_headers "${BOOST_DIR}/test/test_exec_monitor.hpp") else() # Default behavior where header directory is the same as the library name. file(GLOB_RECURSE _boost_unprocessed_headers RELATIVE "${includedir}" "${includedir}/boost/${component}/*") list(INSERT _boost_unprocessed_headers 0 "boost/${component}.hpp") list(REMOVE_ITEM _boost_unprocessed_headers ${_boost_mpi_python_headers} ${_boost_python_numpy_headers}) endif() while(_boost_unprocessed_headers) list(APPEND _boost_processed_headers ${_boost_unprocessed_headers}) foreach(header ${_boost_unprocessed_headers}) if(EXISTS "${includedir}/${header}") file(STRINGS "${includedir}/${header}" _boost_header_includes REGEX "^#[ \t]*include[ \t]*][^>]*>") # The optional whitespace before "#" is intentional # (boost/serialization/config.hpp bug). file(STRINGS "${includedir}/${header}" _boost_header_deps REGEX "^[ \t]*#[ \t]*define[ \t][ \t]*BOOST_LIB_NAME[ \t][ \t]*boost_") foreach(line ${_boost_header_includes}) 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() endforeach() foreach(line ${_boost_header_deps}) string(REGEX REPLACE "^[ \t]*#[ \t]*define[ \t][ \t]*BOOST_LIB_NAME[ \t][ \t]*boost_([^ \t][^ \t]*).*" "\\1" _boost_component_match "${line}") string(REPLACE "python3" "python" _boost_component_match "${_boost_component_match}") string(REPLACE "numpy3" "numpy" _boost_component_match "${_boost_component_match}") list(FIND _boost_DEPS "${_boost_component_match}" _boost_dep_found) if(_boost_component_match STREQUAL "bzip2" OR _boost_component_match 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() if(component STREQUAL "mpi" AND (_boost_component_match STREQUAL "mpi_python" OR _boost_component_match STREQUAL "python")) # Optional python dependency; skip to avoid making it a # hard dependency (handle as special-case for mpi_python). continue() endif() if(component STREQUAL "python" AND _boost_component_match STREQUAL "numpy") # Optional python dependency; skip to avoid making it a # hard dependency (handle as special-case for numpy). continue() endif() if(component STREQUAL "nowide" AND _boost_component_match STREQUAL "filesystem") # Optional filesystem dependency; skip to avoid making it a # hard dependency. continue() endif() if (_boost_dep_found EQUAL -1 AND NOT "${_boost_component_match}" STREQUAL "${component}") list(APPEND _boost_DEPS "${_boost_component_match}") endif() if("${_boost_component_match}" STREQUAL "${component}") set(library_component TRUE) endif() endforeach() endif() endforeach() set(_boost_unprocessed_headers ${_boost_new_headers}) unset(_boost_new_headers) endwhile() # message(STATUS "Unfiltered dependencies for Boost::${component}: ${_boost_DEPS}") if(NOT library_component) unset(_boost_DEPS) endif() set(${_ret_libs} ${_boost_DEPS} PARENT_SCOPE) #string(REGEX REPLACE ";" " " _boost_DEPS_STRING "${_boost_DEPS}") #if (NOT _boost_DEPS_STRING) # set(_boost_DEPS_STRING "(none)") #endif() #message(STATUS "Dependencies for Boost::${component}: ${_boost_DEPS_STRING}") endfunction() message(STATUS "Scanning ${BOOST_DIR}") # List of all directories and files file(GLOB boost_contents RELATIVE "${BOOST_DIR}/boost" "${BOOST_DIR}/boost/*") # Components as directories foreach(component ${boost_contents}) if(IS_DIRECTORY "${BOOST_DIR}/boost/${component}") list(APPEND boost_components "${component}") endif() endforeach() # The following components are not top-level directories, so require # special-casing: # Special-case mpi_python, since it's a part of mpi if(IS_DIRECTORY "${BOOST_DIR}/boost/mpi" AND IS_DIRECTORY "${BOOST_DIR}/boost/python") list(APPEND boost_components "mpi_python") endif() # Special-case numpy, since it's a part of python if(IS_DIRECTORY "${BOOST_DIR}/boost/python" AND IS_DIRECTORY "${BOOST_DIR}/boost/python/numpy") list(APPEND boost_components "numpy") endif() # Special-case wserialization, which is a variant of serialization if(IS_DIRECTORY "${BOOST_DIR}/boost/serialization") list(APPEND boost_components "wserialization") endif() # Special-case math* since there are six libraries, but no actual math # library component. Handle specially when scanning above. # # Special-case separate test libraries, which are all part of test if(EXISTS "${BOOST_DIR}/test/unit_test.hpp") list(APPEND boost_components "unit_test_framework") endif() if(EXISTS "${BOOST_DIR}/test/prg_exec_monitor.hpp") list(APPEND boost_components "prg_exec_monitor") endif() if(EXISTS "${BOOST_DIR}/test/test_exec_monitor.hpp") list(APPEND boost_components "test_exec_monitor") endif() if(boost_components) list(SORT boost_components) endif() # Process each component defined above foreach(component ${boost_components}) string(TOUPPER ${component} UPPERCOMPONENT) _Boost_FIND_COMPONENT_DEPENDENCIES("${component}" "${BOOST_DIR}" _Boost_${UPPERCOMPONENT}_LIBRARY_DEPENDENCIES) endforeach() # Output results foreach(component ${boost_components}) string(TOUPPER ${component} UPPERCOMPONENT) if(_Boost_${UPPERCOMPONENT}_LIBRARY_DEPENDENCIES) string(REGEX REPLACE ";" " " _boost_DEPS_STRING "${_Boost_${UPPERCOMPONENT}_LIBRARY_DEPENDENCIES}") message(STATUS "set(_Boost_${UPPERCOMPONENT}_DEPENDENCIES ${_boost_DEPS_STRING})") endif() endforeach()