Linking to library retrieved with fetchContent

Hello,

I put a library on a server, and I retrieve it with FetchContent :

cmake_minimum_required(VERSION 3.17.3)

project("TestMyLib")

include(FetchContent)
FetchContent_Declare(MyLibrary URL "https://../../MyLibrary.zip")
FetchContent_MakeAvailable(MyLibrary)

FetchContent_GetProperties(MyLibrary)
if(NOT mylibrary_POPULATED)
	message(STATUS "Not populated !")   # Not displayed
endif()

add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME}
    PRIVATE
        main.cpp
)

target_link_libraries(${PROJECT_NAME} PRIVATE MyLibrary)

The zipped archive is a CMake package in config mode, so it contains :

  • a cmake directory with all the cmake config files
    (MyLibraryConfig.cmake,MyLibraryConfigVersion.cmake, MyLibraryTargets.cmake, MyLibraryTargets-debug.cmake, MyLibraryTargets-release.cmake)
  • an include directory, with one directory inside (the library name), and inside all the expected .h
  • a lib directory, with the two binaries (release and debug)

I do generate the project for Visual Studio. Everyting seems fine.
The dowloaded archive is put in :
build\_deps\mylibrary-subbuild\mylibrary-populate-prefix\src,
and its content uncompressed in
build\_deps\mylibrary-src.

But in the Visual Studio project of the client code, the includes and binaries directories of the library are not set. The library binary is set, but the configuration is not correctely set (the reference is to the release configuration instead of debug).

It is not clear for me to understand what I am supposed to do, and what I should expect from FetchContent_MakeAvailable() to make this CMakeLists.txt do what I want.

Note that before retrieving remote artefacts, I used find_package to get the same resource stored locally. And then, when calling target_link_libraries(), I had to refer to the library through a namespace : MyLibrary::MyLibrary. But when using FetchContent, I have to remove the namespace, else I get an error when running CMake (target bot found).

I added the following lines :

FetchContent_GetProperties(MyLibrary SOURCE_DIR mylibrary_SOURCE_DIR)

target_include_directories(${PROJECT_NAME}
    PRIVATE
        ${mylibrary_SOURCE_DIR}/include)

target_link_directories(${PROJECT_NAME}
    PRIVATE
        ${mylibrary_SOURCE_DIR}/lib)

It does work, but I don’t know if it is the good practice, because these two points still need to be resolved (I probably don’t take advantages of some automatic features) :

  • the Debug configuration doesn’t use the Debug configuration of the library (it was automatic when using find_package() on a local package)
  • target_link_libraries() doesn’t have to mention anymore the namespace. I don’t understand why.

the Debug configuration doesn’t use the Debug configuration of the library (it was automatic when using find_package() on a local package)

@craig.scott Is this problem related to this issue ? If so, do you see any workaround ?

You wouldn’t normally use FetchContent to bring in a pre-built binary library. It can be made to work, but it is unusual. Part of the difficulty is that the package will be platform-specific, so your logic would have to provide a platform-specific URL to the FetchContent_Declare() call. Again, that’s doable, but not normal.

FetchContent would normally bring in the sources and build the dependency as part of your main build. If you really do want to have it bring in a pre-built binary instead, then you need to account for the different way that affects the build. FetchContent_MakeAvailable() will look for a CMakeLists.txt in the top level of the downloaded .zip file, but in your case there won’t be one. Your project then becomes responsible for knowing what that unpacked .zip file contains and where to find things within it. Since you mentioned that the .zip file contains a MyLibraryConfig.cmake file, you should be able to call find_package() with the PATHS hard-coded, and that should be all you need to add your pre-built library to your main project. Something like the following should get you close:

cmake_minimum_required(VERSION 3.17.3)
project(TestMyLib)

include(FetchContent)
FetchContent_Declare(MyLibrary URL "https://../../MyLibrary.zip")
FetchContent_MakeAvailable(MyLibrary)

find_package(MyLibrary
    REQUIRED
    NO_DEFAULT_PATH
    PATHS ${mylibrary_SOURCE_DIR}/path/to/dir/containing/*Config.cmake
)

add_executable(TestMyLib main.cpp)
target_link_libraries(TestMyLib PRIVATE MyLibrary::MyLibrary)

You wouldn’t normally use FetchContent to bring in a pre-built binary library. It can be made to work, but it is unusual. Part of the difficulty is that the package will be platform-specific, so your logic would have to provide a platform-specific URL to the FetchContent_Declare() call. Again, that’s doable, but not normal.

Well, do you mean before Conan, the right way was always to build all your dependencies, or install them by hand before calling find_package() ? And in the last case, you also have to adapt your logic according the platform.

PATHS ${mylibrary_SOURCE_DIR}/path/to/dir/containing/*.config

I don’t have *.config file, only *.cmake files.

Sorry, I made a typo in my example, which I’ve now edited to fix the error. It was meant to refer to a *Config.cmake or *-config.cmake file.