Why can't I find curl when cross compiling?

EDIT: to reach more people, cross posted here

Hi all! I’m trying to add curl to my project. I made some tests on native build, and it works fine like this:

find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${CURL_LIBRARIES})

However, I also need to cross-compile my code. Since I am targeting an old Debian stretch system, I compiled the correct version of GCC and glibc, and it works fine for other local libraries. I also made a copy of the target sysroot with this script:

rsync -au --progress \
      --include=/lib/*** \
      --include=/opt/*** \
      --include=/usr \
      --include=/usr/include/*** \
      --include=/usr/lib/*** \
      --include=/usr/local \
      --include=/usr/local/lib/*** \
      --include=/usr/local/include/*** \
      --include=/usr/share \
      --include=/usr/share/cmake-3.7/*** \
      --exclude='*' \
      <target_system>:/ \
      /opt/<local-sysroot>

However, when cross compiling, now configuration fails:

CMake Error at /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
  Could NOT find CURL (missing: CURL_LIBRARY CURL_INCLUDE_DIR) (found version
  "7.81.0")
Call Stack (most recent call first):
  /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)
  /usr/share/cmake-3.22/Modules/FindCURL.cmake:181 (find_package_handle_standard_args)
  CMakeLists.txt:162 (find_package)

notice that 7.81.0 is the version on my host system, while the one present on the target (7.52.1) doesn’t get found. However, my toolchain file contains:

set(root_fs_dir /opt/<local-sysroot>)
set(CMAKE_FIND_ROOT_PATH ${root_fs_dir})
set(CMAKE_SYSROOT ${root_fs_dir})
set(CMAKE_SYSTEM_PREFIX_PATH ${root_fs_dir})

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

thinking it could be due to the different path of CMake module search, I created the symlink

ln -s /opt/box-root-fs/usr/share/cmake-3.7/ /opt/box-root-fs/usr/share/cmake-3.22

but the error is still present

CMake Error at /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
  Could NOT find CURL (missing: CURL_LIBRARY CURL_INCLUDE_DIR) (found version
  "7.81.0")
Call Stack (most recent call first):
  /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)
  /usr/share/cmake-3.22/Modules/FindCURL.cmake:181 (find_package_handle_standard_args)
  CMakeLists.txt:162 (find_package)

so, why can’t CMake find the libraries, if the module file is present? Thank you!

I recommend using the --debug-find flag to configure to figure out what is going on when CMake searches for CURL bits.

1 Like

i cross posted on Stack overflow, and apparently the thing is that the search mechanism of the CMake config files is distributed with CMake, not the libraries being shipped. So, the config files that CMake finds are always relevant to the version of CMake i’m using, not to the version of the libraries in the target root environment. In the end i made it work, and i needed to add (possibly among other things, i don’t remember that well) set(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf)

… apparently the thing is that the search mechanism of the CMake config files is distributed with CMake, not the libraries being shipped.

I think that statement is only partially true. The popularity of a system like cmake depends on its ease of usage. Including dependencies is one of the difficult parts. External libraries are expected to write their own …config.cmake files for support of the find_package command. The more libraries that support cmake, the more populare cmake becomes. But libraries only do that effort if cmake is popular. It is like a chicken and egg problem. So yes, for popular includes the developers of cmake have been so kind to build in mechanism to find packages on your system. This makes cmake more useful even without support from libraries. FindCURL is one of them (see: https://cmake.org/cmake/help/latest/module/FindCURL.html). But similar to FindBoost, the idea is that at some point the libraries will have their own support for cmake. Since cmake 3.30 FindBoost is replace by the regular mechanism if you also use boost +1.71 (or something).

Anyway back on topic, one thing I noticed is that you mention version 3.7 of cmake in the simlink and 3.22. FindCurl is not present in 3.7, but it is in 3.22. also, your curl library is version 7.x. The FindCURL manual states that CURL_INCLUDE_DIRS and CURL_LIBRARIES are only included from version 8.9 and newer. These are exactly the two variables your error message mentions.
So, could it be that the combination of versions in your cross compilation does not support the inclusion? In that case you can fall back to manually defining the paths to mimic the result of the findpackage. (or upgrade your libraries)

I did it like so:

add_library(curllib IMPORTED UNKNOWN)
set_target_properties(curllib
   PROPERTIES
   IMPORTED_LOCATION ${MY_CURL_LIB_LOCATION}
)
target_include_directories(curllib INTERFACE ${MY_CURL_HEADER_LOCATION})
target_link_libraries(curllib INTERFACE z)
add_library(CURL::libcurl ALIAS curllib)

Which is perfect if you have a prebuild version. The two MY_CURL… you need to define in your toolchain file. Or perhaps you can simply build them locally based on the sysroot information.
MY_CURL_LIB_LOCATION points to where the libcurl.so file is located and MY_CURL_HEADER_LOCATION to where the curl.h file is.

If you need to support another external dependency that does not use targets yet (old school), then you can use:

# mimic variable set by find_package - old school.
set(CURL_FOUND true)
set(CURL_VERSION 4.5.0)
set(CURL_INCLUDE_DIRS ${MY_CURL_HEADER_LOCATION})
set(CURL_LIBRARIES  ${MY_CURL_LIB_LOCATION})

(As you can see, I am using an even older version of curl :smiley: )

Note: replace the MY_ with your own personal prefix to avoid naming conflicts.

PS: I am not a cmake guru. I invite anybody to point out the errors of my ways, but this works for me.

include(FetchContent)

FetchContent_Declare(
    CURL
    GIT_TAG curl-8_15_0
    GIT_REPOSITORY https://github.com/curl/curl.git
    # FETCHCONTENT_TRY_FIND_PACKAGE_MODE ALWAYS
    # XXX OVERRIDE_FIND_PACKAGE
    FIND_PACKAGE_ARGS 8.15.0 EXACT NAMES curl CURL
    EXCLUDE_FROM_ALL
    SYSTEM
)
FetchContent_MakeAvailable(CURL)
#XXX find_package(CURL REQUIRED)

@ben.boeckel I am wondering why the find_package() variant use the version 8.14.1 installed on my build host?

Found CURL: /usr/local/Cellar/curl/8.14.1/lib/libcurl.dylib (found version “8.14.1”)

Please use the configure log feature to see what is going on here. You can request debugging for the variable using --debug-find, but --debug-find-pkg=CURL may skip unnecessary logging.

build-dm3e0qdh.log (87.1 KB)

It seems to me the a find_package(CURL) without a previous call to FetchContent_MakeAvailable(CURL) does not use OVERRIDE_FIND_PACKAGE?

set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON)
include(FetchContent)

set(CURL_USE_LIBPSL OFF)
FetchContent_Declare(
    CURL
    GIT_TAG curl-8_15_0
    GIT_REPOSITORY https://github.com/curl/curl.git
    # XXX FETCHCONTENT_TRY_FIND_PACKAGE_MODE ALWAYS
    # implizit! OVERRIDE_FIND_PACKAGE
    FIND_PACKAGE_ARGS 8.15.0 EXACT NAMES curl CURL
    GIT_SHALLOW ON
    EXCLUDE_FROM_ALL
    SYSTEM
)

# mandatory:
FetchContent_MakeAvailable(CURL)

# later optional:
find_package(CURL REQUIRED)

Your comments suggest you’re misunderstanding what FETCHCONTENT_TRY_FIND_PACKAGE_MODE and OVERRIDE_FIND_PACKAGE do. Your example specifies FIND_PACKAGE_ARGS, so you should expect find_package(CURL) to do what it normally does without FetchContent involved first. Only if that doesn’t find CURL will it fall back to trying to fulfil the request using FetchContent. The effect of OVERRIDE_FIND_PACKAGE is essentially the opposite of that. It will redirect any call to find_package(CURL) to immediately be fulfilled by FetchContent instead, essentially bypassing what find_package(CURL) would normally do.

In a call to FetchContent_Declare(), you should never have both FIND_PACKAGE_ARGS and OVERRIDE_FIND_PACKAGE. There is no implicit OVERRIDE_FIND_PACKAGE behavior, other than once a particular dependency has been fulfilled by FetchContent, all subsequent requests for that dependency will redirect to the same one. Only in that case is there going to be behavior that looks like OVERRIDE_FIND_PACKAGE.

The FindCURL.cmake module or FetchContent must be buggy?

set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON)
include(FetchContent)

set(CURL_USE_LIBPSL OFF)
FetchContent_Declare(
    CURL
    GIT_TAG curl-8_15_0
    GIT_REPOSITORY https://github.com/curl/curl.git
    FIND_PACKAGE_ARGS 8.15.0 EXACT NAMES curl CURL
    GIT_SHALLOW ON
    EXCLUDE_FROM_ALL
    SYSTEM
)
find_package(CURL 8.15.0 REQUIRED)

results in:

  The item was not found.

Call Stack (most recent call first):
  CMakeLists.txt:62 (find_package)


CMake Error at /Users/clausklein/.local/share/cmake-4.1/Modules/FindPackageHandleStandardArgs.cmake:227 (message):
  Could NOT find CURL: Found unsuitable version "8.14.1", but required is at
  least "8.15.0" (found /usr/local/Cellar/curl/8.14.1/lib/libcurl.dylib, )
Call Stack (most recent call first):
  /Users/clausklein/.local/share/cmake-4.1/Modules/FindPackageHandleStandardArgs.cmake:589 (_FPHSA_FAILURE_MESSAGE)
  /Users/clausklein/.local/share/cmake-4.1/Modules/FindCURL.cmake:284 (find_package_handle_standard_args)
  CMakeLists.txt:62 (find_package)


CMake Debug Log at CMakeLists.txt:62 (find_package):
  find_package considered the following paths for FindCURL.cmake:

  The file was found at

    /Users/clausklein/.local/share/cmake-4.1/Modules/FindCURL.cmake



-- Configuring incomplete, errors occurred!

@craig.scott How to set up FetchContent_Declare(CURL) so that a find_package(CURL) call is only redirected to the cloned repo if the requested package version is localy not found?

I do not want to code this, but it works:

set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON)
include(FetchContent)

set(CURL_USE_LIBPSL OFF)
FetchContent_Declare(
    CURL
    GIT_TAG curl-8_15_0
    GIT_REPOSITORY https://github.com/curl/curl.git
    GIT_SHALLOW ON
    EXCLUDE_FROM_ALL
    SYSTEM
    # If present, the following keyword and its arguments must be the last
    FIND_PACKAGE_ARGS NAMES curl CURL
)
find_package(CURL 8.15 QUIET)
if(NOT CURL_FOUND)
    FetchContent_MakeAvailable(CURL)
endif()

# later this my be possible ...
find_package(CURL 8.15 REQUIRED)

cmake -S . -B build --debug-find-pkg=CURL --log-level=DEBUG --fresh
build-qgg67e8p.log (112.6 KB)

Can you also please provide CMakeFiles/CMakeConfigureLog.yaml (I may have spelled it wrong)?

CMakeConfigureLog.yaml (1.0 MB)