xtd icon indicating copy to clipboard operation
xtd copied to clipboard

[ENHANCEMENT] Have you used FetchContent before in CMake? It might help with dependencies!

Open codeinred opened this issue 2 years ago • 1 comments

Hi Gammasoft,

Thank you so much for your work again on this library! It's exciting to have a C++ GUI library made for modern C++.

I've spent the morning installing the library, and I noticed it's installing a number of dependencies manually. This is definitely appropriate for dependencies like wxWidgets, which are rather large, but I that a feature added in CMake 3.14 might be able to help with some of the dependency management!

NB: it was originally added in CMake 3.11, but FetchContent_MakeAvailable, which is extremely useful, was added in CMake 3.14

Please see here for a link to the documentation: https://cmake.org/cmake/help/latest/module/FetchContent.html

Essentially, you can use FetchContent to download and link against a library directly from a git repository! I usually use it by wrapping it in a function called find_or_fetch, which will check to see if a package already exists on the system before downloading anything.

Here is a short example that uses find_or_fetch to look for 3 dependencies. If it finds them on the system, it'll use the system version; otherwise, it'll clone the github repository and build & link against that.

    # Find the benchmark library, or download it in build/_deps
    find_or_fetch(
        benchmark
        https://github.com/google/benchmark.git
        main)
    find_or_fetch(
        fmt
        https://github.com/fmtlib/fmt.git
        master)
    find_or_fetch(
        Catch2
        https://github.com/catchorg/catch2.git
        devel)
    FetchContent_MakeAvailable(${remote_dependencies})

This will:

  • Look for the google benchmark library,
  • Look for libfmt,
  • And look for the Catch2 unit test library.

For each one, if it can't find it on the system, then it'll add it to a list called remote_dependencies. It will also clone each github repo into a _deps folder inside the build directory. Once we have all the dependencies, we call FetchContent_MakeAvailable, which will introduce each library into the scope of the current CMakeLists.txt.

Then, we can target the libraries as usual:

target_link_libraries(<your target> 
    benchmark::benchmark 
    fmt::fmt 
    Catch2::Catch2WithMain)

This is the definition of find_or_fetch that I wrote. It provides an ALWAYS_FETCH option that can be passed to CMake if a user wishes to always fetch these libraries, rather than using the system ones.

set(remote_dependencies "")

# If ALWAYS_FETCH is ON, then find_or_fetch will always fetch any remote
# dependencies rather than using the ones provided by the system. This is
# useful for creating a static executable.
option(
    ALWAYS_FETCH
    "Tells find_or_fetch to always fetch packages"
    OFF)


include(FetchContent)
# find_or_fetch will search for a system installation of ${package} via
# find_package. If it fails to find one, it'll use FetchContent to download and
# build it locally.
function(find_or_fetch package repo tag)
    if (NOT ALWAYS_FETCH)
        find_package(${package} QUIET)
    endif()

    if (ALWAYS_FETCH OR NOT ${${package}_FOUND})
        message("Fetching dependency '${package}' from ${repo}")
        
        FetchContent_Declare(
            "${package}"
            GIT_REPOSITORY "${repo}"
            GIT_TAG "${tag}"
        )
        list(APPEND remote_dependencies "${package}")
        set (remote_dependencies  ${remote_dependencies} PARENT_SCOPE)
    else()
        message("Using system cmake package for dependency '${package}'")
    endif()
endfunction()

I just thought I'd share this because it made my life a lot easier, and I thought it might help you too!

codeinred avatar Oct 03 '21 19:10 codeinred

Thanks, I didn't know. It looks interesting, especially your find_or_fetch function. I will look at how to integrate it in xtd.

Regards, Gammasoft

gammasoft71 avatar Oct 04 '21 08:10 gammasoft71