c++cmakesoftware-product-lines

Can CMake find_package be "common dependency version aware"?


The Problem:

I'm in the process of redoing the make system in our legacy project, changing from its present arcane version to CMake. At the moment, I've got CMake treating the whole thing as one large CMake project, but our code base is so large that it's breaking most of the IDEs we throw at it.

We'd like to break it apart, and CMake's find_package "module mode" seems to be ideal for breaking it into "feature-sized" chunks. The prime candidate is a large chunk of the code that only needs to be maintained rarely, and even then its usually by another team. That would allow us to maintain the code, but not continuously recompile it when updating different code.

That said, this chunk of code uses Boost's shared pointer in the API, and while different versions of shared pointer probably will work together, I'd rather not take the chance. So, ideally, the package will be aware of what version of "boost" the system is using, what version of boost was used when the module was compiled, and be able to recompile -- or, at the very least, throw an error or warning in CMake -- if the two don't match.

So ... how does one go about ensuring that versions of common dependencies match in CMake find_package modules? The only thing I can think of is testing the appropriate VERSION variable, but that seems ... bulky. Is there something I'm missing?

Additional Information:

We're using CMake 3.5.1, but we can upgrade to 3.5.2 if that would make a difference. This project is actually a Software Product Line (q.v.), so we are planning to use more modern SPL Software Engineering techniques at some point in the future (yet another reason to choose CMake). The codebase is currently in Redhat Linux, but ideally the technique(s) would be cross-platform.


Solution

  • You may use config mode of the find_package for allow you modules to expose some internal properties to their user (root project).

    If each your module provides library target, you may expose that target with property containing Boost version attached and list this property in a special COMPATIBLE_INTERFACE_STRING property.

    Your root project will include modules via find_package() calls and will read these properties. When it will try to link libraries, provided by such modules, version compatibility will be automatically performed by CMake:

    modA/CMakeLists.txt:

    ...
    find_package(Boost)
    add_library(modA_lib ...)
    ... # Link modA_lib with Boost
    # Install modA_lib target and exports it for use in other project.
    install(TARGETS modA_lib EXPORT modA_lib)
    # Configured -config file is shown below
    configure(modA-config.cmake.in modA-config.cmake)
    install(EXPORT modA_lib
        DESTINATION share/cmake/modA)
    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/modA-config.cmake
        DESTINATION share/cmake/modA)
    

    modA/modA-config.cmake.in:

    include(@CMAKE_INSTALL_PREFIX@/share/cmake/modA/modA_lib.cmake) # Include file described library target
    # Expose linked version of Boost via target's property.
    set_property(TARGET modA_lib PROPERTY INTERFACE_BOOST_VERSION @Boost_VERSION@)
    # Mark this property as compatibility requirement
    set_property(TARGET modA_lib PROPERTY APPEND COMPATIBLE_INTERFACE_STRING BOOST_VERSION)
    

    (modB is implemented in similar manner)

    root/CMakeLists.txt:

    find_package(modA) # This imports target modA_lib
    find_package(modB) # This imports target modB_lib
    
    add_executable(root_exe <...>)
    
    # Boost version check will be performed here
    target_link_libraries(root_exe modA_lib modB_lib)
    

    Additionally, an executable created in the root project may request specific Boost version via setting appropriate property:

    add_executable(root_exe <...>)
    set_property(TARGET root_exe PROPERTY BOOST_VERSION <...>)
    

    In this case it will be prohibited (by CMake) for its dependencies to use Boost library with other versions.

    More info and usage examples see in CMake build system description.