conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.22k stars 979 forks source link

[feature] CMake integration: proof of concept of sysroot + CMakeDeps priority #12478

Open memsharded opened 1 year ago

memsharded commented 1 year ago

It is not fully clear, maybe not enough tested:

What would be the best way to integrate? The conan_toolchain.cmake can include the vendor toolchain.cmake, or an intermediate one from the user, but does this guarantee behavior if the vendor toolchain changes priorities of CMake for finding package only in itself?

cc / @maikelvdh

jcar87 commented 1 year ago

I think with CMake there isn't currently a solution that covers all cases, but there is potential for one.

When not cross compiling, CMakeToolchain will set CMAKE_PREFIX_PATH to the directory where all the generated CMakeDeps files are. In essence, this results in a search order for find_package:

When we are cross compiling (CMAKE_CROSSCOMPILING is True) and there is a CMAKE_SYSROOT defined, the behaviour depends on the value of the CMAKE_FIND_ROOT_PATH_* variables - which currently CMakeToolchain sets to BOTH. That results in the following order:

This search order can cause problems if the sysroot contains things that we intend to consume from Conan instead. For example, it is typical that a target system will contain things like zlib or OpenSSL - in which case, CMake might find the ones from the sysroot even if they are listed as Conan dependencies.

There are also going to be libraries that one won't be providing from Conan, but will rely on the SYSROOT to provide. This is anything that we typically consider "system" libraries (e.g. the GCC runtime libraries, X11, OpenGL, etc).

Obviously if one knows which libraries are to be provided by Conan and which libraries are part of the "system" - then one can alter the find_package() calls accordingly, but as we know this is a level of burden that build system maintainers typically don't like doing.

The "ideal" search order would be:

If CMake had a fourth option in addition to ONLY, NEVER and BOTH, one that is "both, but look in CMAKE_PREFIX_PATH first, then the sysroot, then the rest of the system) - that would probably achieve the behaviour when we are not cross building.

puetzk commented 1 year ago

If CMake had a fourth option in addition to ONLY, NEVER and BOTH, one that is "both, but look in CMAKE_PREFIX_PATH first, then the sysroot, then the rest of the system

You're in luck, but it's very new - part of the CMake 3.24 dependency provider machinery: see CMAKE_FIND_PACKAGE_REDIRECTS_DIR, introduced for exactly this purpose.

puetzk commented 1 year ago
  • Search first in the SYSROOT

  • Then search in the directory set by Conan

  • Then fall back in the host system's directories

Not quite - it actually never searches "in the SYSROOT" per se. Rather, it takes every path it would have searched, in the order it would have searched them, prefixing each with the sysroot (and, if not found there, with other entries in CMAKE_FIND_ROOT_PATH). CMAKE_PREFIX_PATH isn't first, but it is pretty early (only PackageName_ROOT cache keys or environment variables are earlier).

So one workaround (that I've used) is to bind-mount the CONAN_USER_HOME at that same path within the sysroot, so that it, and thus conan's build folders, are found during this prefixed search.

SSE4 commented 1 year ago

that might be confusing, and it needs to be distinguish, for cross-compiling, especially. there are several things involved:

these are traditional values, now CMake variables are mapped to this:

in general, I believe nothing is needed from conan's side, expect population the CMAKE_PREFIX_PATH with conan's directories. otherwise search order can be controlled via CMake variables listed above, as well as more fine grained CMake variables (like individual find modes for programs/libraries/headers, and variables like CMAKE_INCLUDE_PATH, CMAKE_LIBRARY_PATH, CMAKE_PROGRAM_PATH). specially, in case of cross-compilation, when on different stages it needs to use host/build/target tools/libraries.

skycaptain commented 1 year ago

We are using Conan to build libraries and applications for regular desktop machines running Linux, MacOS or Windows, but also for embedded devices running a distribution built with Yocto. In our Conan recipes we have conditions that require OTS packages only, when not cross-compiling. Otherwise, they are expected to be shipped with the Yocto SDK, which by defaults sets this in its toolchain file:

set( CMAKE_FIND_ROOT_PATH $ENV{OECORE_TARGET_SYSROOT} )
set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
set( CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY )

We add Yocto's toolchain file OEToolchainConfig.cmake via tools.cmake.cmaketoolchain:user_toolchain.

The CMAKE_FIND_ROOT_PATH_MODE_* options however prevent CMake finding the -config.cmake files generated by CMakeDeps in the self.generators_path; i.e. CMAKE_PREFIX_PATH will not work as expected. E.g. when building test_package, where we require self.tested_reference_str, we get the following error:

CMake Error at CMakeLists.txt:34 (find_package):
  Could not find a package configuration file provided by
  "my-lib-a" with any of the following names:

    my-lib-aConfig.cmake
    my-lib-a-config.cmake

  Add the installation prefix of "my-lib-a" to
  CMAKE_PREFIX_PATH or set "my-lib-a_DIR" to a directory
  containing one of the above files.  If "my-lib-a" provides
  a separate development package or SDK, be sure it has been installed.

During our investigation to solve this issue, we came to following conclusions:

For Conan we considered two options:

  1. Provide *_DIR for every dependency. We did not pursue this approach any further. Spontaneously, we could not think of a simple solution to implement it.
  2. Add a custom block to our CMakeToolchain to add self.generators_path to CMAKE_FIND_ROOT_PATH, like discussed here.