How to install FILE_SET CXX_MODULES in a generic way?

With this boost project using FILE_SET HEADERS or FILE_SET CXX_MODULES must be installed if used:

# Copyright 2019 Mike Dev
# Distributed under the Boost Software License, Version 1.0.
# See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt

cmake_minimum_required(VERSION 3.21...4.2)

project(boost_any VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)

if(PROJECT_IS_TOP_LEVEL)
    find_package(Boost 1.90.0 CONFIG)
endif()

if(BOOST_USE_MODULES)
    add_library(boost_any)
    target_sources(
        boost_any
        PUBLIC
            FILE_SET modules_public
                TYPE CXX_MODULES
                FILES ${CMAKE_CURRENT_LIST_DIR}/modules/boost_any.cppm
    )

    target_compile_features(boost_any PUBLIC cxx_std_20)
    target_compile_definitions(boost_any PUBLIC BOOST_USE_MODULES)
    if(${CMAKE_CXX_STANDARD} IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD)
        set(CMAKE_CXX_MODULE_STD ON)
        target_compile_definitions(boost_any PRIVATE BOOST_ANY_USE_STD_MODULE)
        message(STATUS "Using `import std;`")
    else()
        message(WARNING "`import std;` is not available")
    endif()
    set(__scope PUBLIC)

    # TODO(CK): unresolved with shared libs?
    # add_executable(module_sample modules/usage_sample.cpp)
    # target_link_libraries(module_sample PRIVATE boost_any)
else()
    set(CMAKE_VERIFY_INTERFACE_HEADER_SETS ${PROJECT_IS_TOP_LEVEL})
    add_library(boost_any INTERFACE)
    set(__scope INTERFACE)
endif()

if(CMAKE_VERSION VERSION_LESS 3.23)
    target_include_directories(boost_any ${__scope} include)
else()
    target_sources(
        boost_any
        ${__scope}
        FILE_SET headers_public
        TYPE HEADERS
        BASE_DIRS include
        FILES
            include/boost/any/bad_any_cast.hpp
            include/boost/any/basic_any.hpp
            include/boost/any/fwd.hpp
            include/boost/any/unique_any.hpp
            include/boost/any/detail/config.hpp
            include/boost/any/detail/placeholder.hpp
    )
    if(NOT BOOST_USE_MODULES)
        target_compile_features(boost_any ${__scope} cxx_std_17)
    endif()
endif()

if(PROJECT_IS_TOP_LEVEL)
    target_link_libraries(
        boost_any
        ${__scope}
            Boost::headers
    )
else()
    target_link_libraries(
        boost_any
        ${__scope}
            Boost::config
            Boost::throw_exception
            Boost::type_index
    )
endif()

add_library(Boost::any ALIAS boost_any)

if(BUILD_TESTING)
    add_subdirectory(test)
endif()

So I have to change the install rules at Fix: install FILE_SET too if needed by ClausKlein · Pull Request #1 · anarthal/boost-cmake · GitHub

@pdimov @anarthal what to you think about this possible solution?

@craig.scott with this changes it works with and w/o CXX_MODULES, but I am missing the installed BMI files?

diff --git a/include/BoostInstall.cmake b/include/BoostInstall.cmake
index 23e3a46..1f3f7be 100644
--- a/include/BoostInstall.cmake
+++ b/include/BoostInstall.cmake
@@ -56,7 +56,7 @@ endif()
 #
 
 if(CMAKE_SOURCE_DIR STREQUAL "${BOOST_SUPERPROJECT_SOURCE_DIR}" AND NOT __boost_install_status_message_guard)
-  message(STATUS "Boost: using ${BOOST_INSTALL_LAYOUT} layout: ${CMAKE_INSTALL_INCLUDEDIR}, ${CMAKE_INSTALL_BINDIR}, ${CMAKE_INSTALL_LIBDIR}, ${BOOST_INSTALL_CMAKEDIR}")
+  message(STATUS "Boost: using ${BOOST_INSTALL_LAYOUT} layout: ${CMAKE_INSTALL_INCLUDEDIR}, ${CMAKE_INSTALL_BINDIR}, ${CMAKE_INSTALL_LIBDIR}, ${BOOST_INSTALL_CMAKEDIR}, ${CMAKE_INSTALL_DATADIR}")
   set(__boost_install_status_message_guard TRUE)
 endif()
 
@@ -297,6 +297,17 @@ function(boost_install_target)
     string(APPEND CONFIG_INSTALL_DIR "-static")
   endif()
 
+  get_target_property(INTERFACE_CXX_MODULE_SETS ${LIB} INTERFACE_CXX_MODULE_SETS)
+  if(INTERFACE_CXX_MODULE_SETS)
+    boost_message(DEBUG "boost_install_target: '${__TARGET}' has INTERFACE_CXX_MODULE_SETS=${INTERFACE_CXX_MODULE_SETS}")
+    set(__INSTALL_CXX_MODULES FILE_SET ${INTERFACE_CXX_MODULE_SETS} DESTINATION ${CMAKE_INSTALL_DATADIR})
+  endif()
+  get_target_property(INTERFACE_HEADER_SETS ${LIB} INTERFACE_HEADER_SETS)
+  if(INTERFACE_HEADER_SETS)
+    boost_message(DEBUG "boost_install_target: '${__TARGET}' has INTERFACE_HEADER_SETS=${INTERFACE_HEADER_SETS}")
+    set(__INSTALL_HEADER_SETS FILE_SET ${INTERFACE_HEADER_SETS})
+  endif()
+
   install(TARGETS ${LIB} EXPORT ${LIB}-targets
     # explicit destination specification required for 3.13, 3.14 no longer needs it
     RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
@@ -304,9 +315,18 @@ function(boost_install_target)
     ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
     PRIVATE_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
     PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+    # explicit needed if used starting with cmake v3.28
     FILE_SET CXX_MODULES DESTINATION "${CMAKE_INSTALL_DATADIR}"
+    ${__INSTALL_CXX_MODULES}
+    # Any module files from C++ modules from PUBLIC sources in a file set of type CXX_MODULES will be installed to the given DESTINATION.
+    # FIXME: why does this silently not work here? CK
+    CXX_MODULES_BMI DESTINATION ${CONFIG_INSTALL_DIR}/bmi-${CMAKE_CXX_COMPILER_ID}_$<CONFIG>
+    # explicit needed if used starting with cmake v3.23
+    FILE_SET HEADERS
+    ${__INSTALL_HEADER_SETS}
   )
 
+  # TODO(CK): what is this for?
   export(TARGETS ${LIB} NAMESPACE Boost:: FILE export/${LIB}-targets.cmake)
 
   if(MSVC)
@@ -319,7 +339,7 @@ function(boost_install_target)
     endif()
   endif()
 
-  install(EXPORT ${LIB}-targets DESTINATION "${CONFIG_INSTALL_DIR}" NAMESPACE Boost:: FILE ${LIB}-targets.cmake)
+  install(EXPORT ${LIB}-targets DESTINATION "${CONFIG_INSTALL_DIR}" NAMESPACE Boost:: FILE ${LIB}-targets.cmake CXX_MODULES_DIRECTORY .)
 
   set(CONFIG_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/tmpinst/${LIB}-config.cmake")
   set(CONFIG_FILE_CONTENTS "# Generated by BoostInstall.cmake for ${LIB}-${__VERSION}\n\n")

As I explain in my comment in the other post, this is solved in Boost.CMake but it’s still in a feature branch. We will hopefully merge it once CMake ships import std as stable. Boost.Any is not part of the original module proposal. I’d prefer if you could make comments on the feature branches I linked in the other post (particularly the one in Boost.Mp11), as I think that’s gonna be more useful for us. Thanks.