Search code examples
c++cmakeseal

Using FetchContent_Declare together with CMAKE_ARGS


I'm using cmake v3.13 and I want to change my ExternalProject_Add() for the SEAL library to:

include(FetchContent)
# Get the seal library
set(SEAL "seal")
FetchContent_Declare(
        ${SEAL}
        GIT_REPOSITORY  https://github.com/microsoft/SEAL
        GIT_TAG         v3.5.2

)
FetchContent_GetProperties(${SEAL})
if(NOT ${SEAL}_POPULATED)
    FetchContent_Populate(${SEAL})
    add_subdirectory(${${SEAL}_SOURCE_DIR} ${${SEAL}_BINARY_DIR})
endif()

When I was using ExternalProject_Add() I've used CMAKE_ARGS -DBUILD_SHARED_LIBS=ON and this doesn't work with FetchContent_Declare() that only downloads the library.

The SEAL v3.5.2 CMakeLists.txt uses this to check if a shared library needs to be built:

# Should we build also the shared library?
set(BUILD_SHARED_LIBS_STR "Build shared library")
option(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_STR} OFF)
if(MSVC AND BUILD_SHARED_LIBS)
    message(WARNING "This build system only supports a static build; disabling `BUILD_SHARED_LIBS`")
    set(BUILD_SHARED_LIBS OFF CACHE BOOL ${BUILD_SHARED_LIBS_STR} FORCE)
endif()

# Conditionally build the shared library
if(BUILD_SHARED_LIBS)
    add_library(seal_shared SHARED $<TARGET_OBJECTS:seal_obj>)
    set_target_properties(seal_shared PROPERTIES OUTPUT_NAME seal)
    seal_set_version(seal_shared)
    seal_set_soversion(seal_shared)
    seal_set_language(seal_shared)
    seal_set_include_directories(seal_shared)
    seal_link_threads(seal_shared)

    # Conditionally add MSGSL include directory to build interface
    if(SEAL_USE_MSGSL AND NOT MSVC)
        target_include_directories(seal_shared PUBLIC $<BUILD_INTERFACE:${MSGSL_INCLUDE_DIR}>)
    endif()

    if(SEAL_USE_ZLIB AND NOT MSVC)
        # In the shared build we link zlibstatic into the shared library
        target_link_libraries(seal_shared PRIVATE zlibstatic)
    endif()

    seal_install_target(seal_shared SEALTargets)
endif()

Is there a way to download the SEAL library using FetchContent_Declare() and then use some CMakeLists setting to pass the CMAKE_ARGS -DBUILD_SHARED_LIBS=ON argument to the downloaded library when building it?


Solution

  • When build some project at the top-level, you may pass a parameter to it using command line option

    -D<VARIABLE>=<VALUE>
    

    (ExternalProject_Add builds the project "as if" top-level, so the option passing is technically the same).

    When build some project as a subproject using add_subdirectory approach, you may use the same command line option

    -D<VARIABLE>=<VALUE>
    

    for top-level project, and this parameter will be propagated to the subproject too.

    If passing the parameter to the top-level project is not desired, then you may emulate the parameter setting inside CMakeLists.txt using set(CACHE INTERNAL) command flow:

    set(<PARAMETER> <VALUE> CACHE INTERNAL "<some description>")
    

    Make sure this line is issued before add_subdirectory() call (otherwise it won't affect the subproject).

    So in your case you may use following code:

    if(NOT ${SEAL}_POPULATED)
        FetchContent_Populate(${SEAL})
        # Make subproject to use 'BUILD_SHARED_LIBS=ON' setting.
        set(BUILD_SHARED_LIBS ON CACHE INTERNAL "Build SHARED libraries")
        add_subdirectory(${${SEAL}_SOURCE_DIR} ${${SEAL}_BINARY_DIR})
    endif()
    

    All above works perfectly when top-level project doesn't use the parameter set for subproject.

    If both top-level project and subproject are affected by the same parameter, and you want to hardcode the parameter for the subdproject only, then things become more complicated. You need to restore the parameter after add_subdirectory call:

    if(NOT ${SEAL}_POPULATED)
        FetchContent_Populate(${SEAL})
    
        # Store the old value of the 'BUILD_SHARED_LIBS'
        set(BUILD_SHARED_LIBS_OLD ${BUILD_SHARED_LIBS})
        # Make subproject to use 'BUILD_SHARED_LIBS=ON' setting.
        set(BUILD_SHARED_LIBS ON CACHE INTERNAL "Build SHARED libraries")
    
        add_subdirectory(${${SEAL}_SOURCE_DIR} ${${SEAL}_BINARY_DIR})
    
        # Restore the old value of the parameter
        set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_OLD} CACHE BOOL "Type of libraries to build" FORCE)
    endif()
    
    # ...
    
    # The library will be created according to "original" value for BUILD_SHARED_LIBS option.
    add_library(top_lib top_lib.c)
    

    Note, that in case of restoring parameter, set(CACHE TYPE FORCE) command flow is used instead of set(CACHE INTERNAL). This restores not only a value of the CACHE variable, but also its type, which is shown in CMake GUI.