FindCURL doesn't set CURL_LIBRARIES and CURL_INCLUDE_DIRS

I have build the libcurl from source with cmake and installed to /usr/local/ and I used find_package(CURL 7.88.1 EXACT REQUIRED) in my project’s main CMakeLists.txt. It find CURL with 7.88.1 version, however, when I want to print CURL_LIBRARIES and CURL_INCLUDE_DIRS in CMakeLists.txt prints nothing. Furthermore, I tried to link the libcurl with CURL::libcurl which is set in CURLTargets.cmake still I can’t see it linked shared to my binary.

find_package(CURL 7.88.1 EXACT REQUIRED)

message("curl libs: ${CURL_LIBRARIES}")
message("curl dirs: ${CURL_INCLUDE_DIRS}")

...

target_include_directories(${PROJECT_NAME}
    PUBLIC
        ${Boost_INCLUDE_DIRS}
        ${CURL_INCLUDE_DIRS}
        ${RAPIDJSON_INCLUDE_DIRS}
        ${ZeroMQ_INCLUDE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/include)

target_link_libraries(${PROJECT_NAME}
                        ${Boost_LIBRARIES}
                        spdlog::spdlog
                        CURL::libcurl
                        ${CURL_LIBRARIES}
                        ${ZeroMQ_LIBRARY})

output:

-- Found CURL: /usr/local/lib/cmake/CURL/CURLConfig.cmake (found suitable exact version "7.88.1")  
curl libs: 
curl dirs: 

Fix: It is possible by linking the libcurl to my project by CURL::libcurl as stated in cmake reference under section IMPORTED Targets. But it doesn’t set the variables as it stated.

cmake reference: FindCURL

OS: Windows subsystem linux Ubuntu 20.04

CMake Version: 3.19.8

It looks like upstream ends up being used, it does not provide those variables. I suppose we could try to set these variables, but it is likely not possible to be 100% accurate without using a generator expression.

FindPackage has two modes: Module Mode and Config Mode (as stated in https://cmake.org/cmake/help/latest/command/find_package.html#search-modes)

In Module Mode cmake will use a FindCurl.cmake file either provided by cmake itself (https://cmake.org/cmake/help/latest/module/FindCURL.html) or by yourself (in this case you would have such a script in your repo).

And then there is Config mode, where Cmake searches for a CURLConfig.cmake which is provided by the project itself and thus is somehwere installed on your system usually. This is the modern and recommended approach. So if a library supports that (and unfortunatly many don’t) then use this.

Curl supports the config mode and as you can in your output it found this file /usr/local/lib/cmake/CURL/CURLConfig.cmake. Thus is not using the script provided by cmake (https://cmake.org/cmake/help/latest/module/FindCURL.html)! And thus completly different targets or variables might be set.
You can change that and use the cmake file, but you really shouldn’t,

And then there is one more thing. The old school cmake way of doing things is to have variables such as ${Boost_INCLUDE_DIRS} and ${Boost_LIBRARIES}. These contain the include headers and libraries. And then you would use target_include_directories and target_link_libraries and such stuff. This is how it used to be and quite often you will find thus find.cmake scripts which set these variables only. But this isn’t how you should do it.

Instead modern cmake works with targets. Each target knows its include directories, libraries, compiler requirements. It specifies with PUBLIC, INTERFACE or PRIVATE whether that information should be inherited to targets linking against the target. So the only thing you should do is link against the target and you will get all the inlcude directories and stuff automatically.

Ideally your code should look similar to this:

target_include_directories(my_target PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(my_target PUBLIC spdlog::spdlog CURL::libcurl rapidjson::rapidjson boost::headers)

So don’t set any include directories that are not your own. Don’t link any rare libraries, but always cmake targets.

Most of the libraries that support config mode of find package provide such targets. The Find.cmake scripts often don’t. But the one for curl does and the one for boost as well (https://cmake.org/cmake/help/latest/module/FindBoost.html#imported-targets).
And for your own scripts or the ones you got from some github repository you can change them so they use imported targets instead.

For example:

This is an older FindProj.cmake file which uses the variables and defines no target.

But here is a newer version which defines these targets:

So in summary:

  • Try to always use Config Mode if available
  • Really Really Try hard to use modern cmake targets and don’t work with these variables anymore. At least not the way you use them right now.
  • If some project doesn’t support config mode, file an issue at least and ideally make a PR to fix that. Same if they have some bug in it (like not defining all include dirs or such stuff)
  • If you use Find.cmake scripts update them to use imported targets
  • If you publish your own projects, then ensure they support config mode and define proper targets.

It’s really annoying that so many projects still use old cmake code. But you can help by investing some time in cmake (understanding how the whole stuff works with findPackage, targets, …), open up issues on the projects, fix some yourself, don’t do the same mistakes in your own published projects.

I do hope that the cmake teams implements some solutions in the future that make it easier to do stuff wrong.

1 Like

I really didn’t know that linking against the target automatically includes the dirs. That is really good knowledge for me I will fix my CMakeLists. Thank you for reply.