CPM.cmake icon indicating copy to clipboard operation
CPM.cmake copied to clipboard

Can CPM be used to build interdependent libs ?

Open shrkamat opened this issue 3 years ago • 10 comments

This is not a bug report instead a ask for feasibility / documentation (examples).

Let say I need to build curl, that's in turn dependent on openssl & zlib. Curl CMakeLists.txt uses find_package for OpenSSL and ZLIB

Can I make use of CPMAddPackage for all these modules (zlib, openssl, curl) in a single CMake project ?

shrkamat avatar Apr 26 '22 18:04 shrkamat

Reading a bit more on this -

https://gitlab.kitware.com/cmake/cmake/-/issues/22619 https://gitlab.kitware.com/cmake/cmake/-/issues/21687

shrkamat avatar Apr 26 '22 20:04 shrkamat

CPM.cmake automatically creates FindXXX.cmake Modules, so I believe you should just be able to use find_package(openssl) after adding it with CPMAddPacakge. However, any specific options passed to find_package will likely not be supported.

TheLartians avatar May 16 '22 19:05 TheLartians

I am also trying to build curl with cpm. In my case I try to build nghttp2 first, which is required by curl.

set(nghttp2_version         "1.46.0")
cpmaddpackage(
  NAME
  nghttp2
  OPTIONS
  "ENABLE_STATIC_LIB ON"
  "BUILD_SHARED_LIBS OFF"
  "ENABLE_EXAMPLES OFF"
  URL
  "https://github.com/nghttp2/nghttp2/releases/download/v${nghttp2_version}/nghttp2-${nghttp2_version}.tar.gz"
)

set(curl_version            "7_82_0")
string(REPLACE "_" "." curl_version_dot ${curl_version})
cpmaddpackage(
  NAME
  curl
  OPTIONS
  "BUILD_CURL_EXE OFF"
  "CURL_USE_LIBSSH2 OFF"
  "ENABLE_ARES OFF"
  "ENABLE_THREADED_RESOLVER OFF"
  "HTTP_ONLY ON"
  "USE_ZLIB ON"
  "USE_NGHTTP2 ON"
  "BUILD_SHARED_LIBS OFF"
  "OPENSSL_ROOT_DIR ${OPENSSL_ROOT_DIR}"
  URL
  "https://github.com/curl/curl/releases/download/curl-${curl_version}/curl-${curl_version_dot}.tar.gz"
)

But now at 'cmake configure' time, I get this build error:

-- CPM: adding package [email protected] (7.82.0)
-- curl version=[7.82.0]
-- Found OpenSSL: /opt/homebrew/opt/[email protected]/lib/libcrypto.dylib (found version "1.1.1n")  
CMake Error at /opt/homebrew/Cellar/cmake/3.22.2/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
  Could NOT find NGHTTP2 (missing: NGHTTP2_LIBRARY NGHTTP2_INCLUDE_DIR)
Call Stack (most recent call first):
  /opt/homebrew/Cellar/cmake/3.22.2/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)
  build/_deps/curl-src/CMake/FindNGHTTP2.cmake:28 (find_package_handle_standard_args)
  build/_deps/curl-src/CMakeLists.txt:504 (find_package)

Because building first the dependency (nghttp2) and installing its header is required by the curl 'configure' step.

How can I solve this ?

bsergean avatar Aug 03 '22 00:08 bsergean

@bsergean Did you ever find a solution for this?

shadowpsi avatar Oct 12 '22 05:10 shadowpsi

Not yet, but I will probably build libnghttp2 very early in my build process. I'm gonna be working on this soon so I'll post what I find.

bsergean avatar Oct 12 '22 05:10 bsergean

I can get it compiling with

if (curl_ADDED)
    if (EXISTS ${curl_SOURCE_DIR}/CMake/FindNGHTTP2.cmake)
        # Not sure how to do this at config time?
        file(RENAME ${curl_SOURCE_DIR}/CMake/FindNGHTTP2.cmake ${curl_SOURCE_DIR}/CMake/FindNGHTTP2.cmake.bak)
    endif()
    get_all_targets(curl_targets ${curl_SOURCE_DIR})
    list(FILTER curl_targets EXCLUDE REGEX "test.*")
    list(REMOVE_ITEM curl_targets show)
    foreach (ct ${curl_targets})
        target_include_directories(${ct} PRIVATE ${NGHTTP2_SOURCE_DIR}/lib/includes)

    endforeach()
endif()

where get_all_targets is

function(get_all_targets var dir)
    set(targets)
    get_all_targets_recursive(targets ${dir})
    set(${var} ${targets} PARENT_SCOPE)
endfunction()

macro(get_all_targets_recursive targets dir)
    get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)
    foreach(subdir ${subdirectories})
        get_all_targets_recursive(${targets} ${subdir})
    endforeach()

    get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS)
    list(APPEND ${targets} ${current_targets})
endmacro()

Any clues how to remove a file before it configures curl?

shadowpsi avatar Oct 12 '22 07:10 shadowpsi

What I do to support use case of library doing find_package, but you want to provide your target:

# In the library CMakeLists
if(NOT TARGET my_target)
  find_package(my_target)
endif()

So in original library, this is one PR that needs to get accepted. Depending if they want to support super build it can be easy to convince or not. Anyway it is a single atomic change that shouldn't break anything.

Then you to your cpmaddpackage(my_target) then cpmaddpackage(targetthatuses_my_target).

This come with the need that the find_package define targets and is "modern".

All in all, to avoid ugly hack that will break and are hard to maintain it's better to use library with good CMakeLists. Like said in readme:

Dependent on good CMakeLists Many libraries do not have CMakeLists that work well for subprojects. Luckily this is slowly changing, however, until then, some manual configuration may be required (see the snippets below for examples). For best practices on preparing projects for CPM, see the wiki.

OlivierLDff avatar Oct 12 '22 07:10 OlivierLDff

Are you saying that you would need a small change in Curl for this to work ? Maybe we should make a PR there it that’s the case, and the code change is not too bad.

FYI I build curl with c-ares and nghttp2 (its dependencies).

On Oct 12, 2022, at 12:28 AM, Olivier Le Doeuff @.***> wrote:

What I do to support use case of library doing find_package, but you want to provide your target:

In the library CMakeLists

if(NOT TARGET my_target) find_package(my_target) endif() So in original library, this is one PR that needs to get accepted. Depending if they want to support super build it can be easy to convince or not. Anyway it is a single atomic change that shouldn't break anything.

Then you to your cpmaddpackage(my_target) then cpmaddpackage(targetthatuses_my_target).

This come with the need that the find_package define targets and is "modern".

All in all, to avoid ugly hack that will break and are hard to maintain it's better to use library with good CMakeLists. Like said in readme:

Dependent on good CMakeLists Many libraries do not have CMakeLists that work well for subprojects. Luckily this is slowly changing, however, until then, some manual configuration may be required (see the snippets below https://github.com/cpm-cmake/CPM.cmake#snippets for examples). For best practices on preparing projects for CPM, see the wiki https://github.com/cpm-cmake/CPM.cmake/wiki/Preparing-projects-for-CPM.cmake.

— Reply to this email directly, view it on GitHub https://github.com/cpm-cmake/CPM.cmake/issues/348#issuecomment-1275712269, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC2O6UNTG7TKZXBFTMBZI3DWCZSAFANCNFSM5UMWF4JA. You are receiving this because you were mentioned.

bsergean avatar Oct 12 '22 13:10 bsergean

I didn't dig in the code or tried myself, but this is my general goto solution if the find_package return a target, and the rest of the cmake script depends on target. Otherwise it sounds like more hack.

OlivierLDff avatar Oct 12 '22 13:10 OlivierLDff

Yes, however as stated in one of those links

In my case, 70% of my dependencies didn’t define any targets in their find modules or configs. The reality is that CMake usage is an anarchy.

So it seems ugly hacks are required in 70% of cases. One recurring issue I'm running into is what to do where libB expects something from libA that is only created at compile time. I.e. AHeaderVersion.h.in --> install/include/AHeaderVersion.h

What I have been doing (which basically just gives me download caching) is something more like this:

include(ExternalProject)

set(nghttp2_version         "1.46.0")
cpmaddpackage(
  NAME
  NGHTTP2
  URL
  "https://github.com/nghttp2/nghttp2/releases/download/v${nghttp2_version}/nghttp2-${nghttp2_version}.tar.gz"
  DOWNLOAD_ONLY YES
)

if (NGHTTP2_ADDED)
    ExternalProject_Add(NGHTTP2_EPA
        SOURCE_DIR ${NGHTTP2_SOURCE_DIR}
        CMAKE_ARGS 
        -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}
        -DENABLE_STATIC_LIB=ON
        -DBUILD_SHARED_LIBS=OFF
        -DENABLE_EXAMPLES=OFF
        )
endif()

set(curl_version            "7_82_0")
string(REPLACE "_" "." curl_version_dot ${curl_version})
cpmaddpackage(
  NAME
  curl
  URL
  "https://github.com/curl/curl/releases/download/curl-${curl_version}/curl-${curl_version_dot}.tar.gz"
  DOWNLOAD_ONLY YES
)

if (curl_ADDED)
    ExternalProject_Add(curl_EPA
        SOURCE_DIR ${curl_SOURCE_DIR}
        DEPENDS NGHTTP2_EPA
        CMAKE_ARGS
        -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}
        -DBUILD_CURL_EXE=OFF
        -DCURL_USE_LIBSSH2=OFF
        -DENABLE_ARES=OFF
        -DENABLE_THREADED_RESOLVER=OFF
        -DHTTP_ONLY=ON
        -DUSE_ZLIB=ON
        -DUSE_NGHTTP2=ON
        -DBUILD_SHARED_LIBS=OFF
        -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}
        )
endif()

Then I have to manually build IMPORTED targets after that. I.e. giant ugly hack

shadowpsi avatar Oct 12 '22 23:10 shadowpsi