Distributing C++20 modules as source

Hi all,

This is a follow-up of Advice on C++20 modules (Boost) - #13 by ben.boeckel. I’m writing a proof of concept with Boost, where every library becomes a module.

Just to illustrate, this is would be the CMake code for Boost.Variant2, which depends on Boost.assert, Boost.config and Boost.mp11:

  add_library(boost_variant2)
  target_sources(boost_variant2 PUBLIC FILE_SET CXX_MODULES FILES modules/variant2.cxx)
  target_include_directories(boost_variant2 PUBLIC include)
  target_compile_features(boost_variant2 PUBLIC cxx_std_23)
  set_target_properties(boost_variant2 PROPERTIES CXX_MODULE_STD 1)
  target_compile_definitions(boost_variant2 PRIVATE BOOST_CXX20_MODULE)
  target_compile_options(boost_variant2 PUBLIC -Wno-include-angled-in-module-purview) # TODO: scope this to clang
  target_link_libraries(boost_variant2
    PUBLIC
      Boost::assert
      Boost::config
      Boost::mp11
  )

Where targets like Boost::mp11 and Boost::assert are libraries added to the project just like the one shown above (and are thus C++20 modules, too). Everything uses the experimental import std feature in CMake.

The libraries get installed by the Boost main CMake script, with calls akin to:

  install(TARGETS ${LIB} EXPORT ${LIB}-targets
    # explicit destination specification required for 3.13, 3.14 no longer needs it
    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    PRIVATE_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
    PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
    FILE_SET CXX_MODULES DESTINATION .
  )

While this is good, most of the above libraries are header-only. The add_library call generates a static library containing little more than the module initializer. Ideally, the modules for these libraries should follow a mechanism similar to standard library modules: we would distribute the headers and the module files, and the modules would be built entirely by the consuming project, using the optimization level, language dialect and other flags required by the target - following the same mechanics as standard modules.

I appreciate that this is still a work in progress even for standard modules, so it may still be a long way. But I’d like to know whether something like I describe is on the roadmap.

Syntax like the following would be convenient:

# Don't build any library straight away. Build it as required by dependent targets
add_library(boost_variant2 INTERFACE)
target_sources(boost_variant2 INTERFACE FILE_SET CXX_MODULES FILES modules/variant2.cxx)

Any suggestions on how to do this would be welcome.

Thanks,
Ruben.