CPM.cmake
                                
                                
                                
                                    CPM.cmake copied to clipboard
                            
                            
                            
                        Can CPM be used to build interdependent libs ?
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 ?
Reading a bit more on this -
https://gitlab.kitware.com/cmake/cmake/-/issues/22619 https://gitlab.kitware.com/cmake/cmake/-/issues/21687
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.
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 Did you ever find a solution for this?
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.
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?
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.
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.
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.
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