17

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?

1
  • @Tsyvarev I don't set BUILD_SHARED_LIBS to any other value so their default setting should create the libseal.so in the /cmake-build-debug/_deps/seal-src/lib/ path but all I get there is the static libseal-3.5.a. Commented May 30, 2020 at 11:26

1 Answer 1

22

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.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you for the detailed explanation! Could you please explain add_library() I've tried add_library(${SEAL}_LIB ${${SEAL}_SOURCE_DIR}) with target_link_libraries(hmmenc-client PRIVATE ${SEAL}_LIB), but I only get CMake Error: Cannot determine link language for target "seal_LIB". CMake Error: CMake can not determine linker language for target: seal_LIB. I also didn't see the libseal.so anywhere that was supposed to be created with BUILD_SHARED_LIBS ON.
The second snippet describes the case when your (main, top-level) project also has add_library calls which do not specify SHARED/STATIC option and thus depends on BUILD_SHARED_LIBS setting. If you don't have such calls, then resort to the first snippet: it is simpler. Note, that in CMake add_library takes list of source files. Passing to it ${${SEAL}_SOURCE_DIR}, which denotes a directory, is illegal.
Thanks! I left the second snippet and removed the add_library() part. I find it not a bad practice to restore the values back to their original settings especially, if they were supposed to be set only for one project part. Anyway, now I see libseal.so* under /cmake-build-debug/_deps/seal-src/lib/
Thank you very much for explaining the difference between both variants regarding CMAKE_ARGS, I searched an eternity and found solutions for ExternalProject_Add and I got crazy confused why it didn't work for FetchContent_Declare. You, Sir, are a life saver!
set(<PARAMETER> <VALUE> CACHE INTERNAL "<some description>") implies <VALUE> is STRING type, so for BOOL type it might be improper.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.