Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using FetchContent_Declare together with CMAKE_ARGS

Tags:

c++

cmake

seal

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?

like image 566
TalG Avatar asked May 30 '20 11:05

TalG


People also ask

How does it work with CMake and fetch content?

It uses FetchContent to include the libraries’ doctest and range-v3. CMake will download and build the dependencies. Very convenient! I will explain how everything works below. Make sure you get the code to play around with it. We begin by creating a regular CMake project.

Will we have to stick to submodules when configuring a CMake project?

This means that CMake downloads and builds your dependencies after the generation step. So your dependencies will not be available yet when CMake configures your project. Will we have to stick to submodules then? No, we won’t! With version 3.11 CMake introduced a new module: FetchContent.

Why does fetchcontent fail to install cmakelists?

Every once in a while you will come across libraries that are missing the call to install () in their CMakeLists.txt. In this case, FetchContent does not know how to copy the built code into the install folder and will fail. In this case, consider adding the install () calls and creating a PR.

How do I use external project in CMake?

ExternalProject wraps dependencies into a CMake target and allows managing foreign code from your CMakeLists.txt. To use it, one must add a target via ExternalProject_Add (). CMake will then run the following steps for this target. Download the dependency. Here one can use a version control system or download from an URL.


Video Answer


1 Answers

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.

like image 108
Tsyvarev Avatar answered Oct 09 '22 16:10

Tsyvarev