question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[question] CMakeDeps + CmakeToolchain collision with package find modules

See original GitHub issue

Is there a way to tell CMakeDeps to not generate a config file for certain dependencies from the consumer side? Also is there a way to make the CMakeToolchain add the package paths to the module path, like cmake_paths generator did?

I’m using the package for pybind11, which ships its own pybind11-config.cmake, which contains useful macros. I’m also seeing a file by the same name generated via the CMakeDeps, of course without the macros. The toolchain only appends CMAKE_BINARY_DIR to the module path, so the generated one is found when I need to find the one within the package.

More generally:

CMake’s find_package searches the module path to find FindFoo.cmake or Foo-config.cmake. In the context of conan (assuming I want to only find conan packages, not system-installed libs), there are a few places such files would normally be found:

  • Within the CMakes installation’s modules folder
  • Within a conan package folder in the cache. This is the case if:
    • The library itself provides its own Foo-config.cmake. Mature libraries based on CMake are advised to provide this.
    • The recipe for Foo exports a FindFoo.cmake.
  • Within CMAKE_BINARY_DIR when CMakeDeps generator is used

When using CMakeDeps + CMakeToolchain, only the last one is effectively possible. Other generators, like cmake_paths add package folders to the module path, but this is no the case with CMakeDeps + CMakeToolchain. However, conan intends to sunset cmake_paths and other cmake generators in favor of CMakeDeps.

The config files within the package should take precedent because it has been specialized for the particular package and provides necessary macros. To enable this, I believe CMakeToolchain needs to add the package folders to the module path, like cmake_paths does, and CMakeDeps needs a way to be prevented from generating config files for individual packages.

You could consider this partly a bug in the recipe for Foo. The recipe should set self.cpp_info.set_property("skip_deps_file", True) if it is providing its own config file. However, it would be super convenient to affect this from the consumer side so I don’t have to fork the recipe. Even after that, I have to do some manual steps to get the Foo package folder in the module path.

Less critically, I’d also expect that the default find-modules in the cmake installation should take precedent over the generated config files. CMakeToolchain has defeated this by setting set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON). I believe I can override this in my generate method, but I’m surprised the default is to take the generated config files over the handcrafted ones.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:11 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
kenfredcommented, Aug 16, 2021

Thanks @lasote!

No, we think that the config file to be used should be always the one generated by Conan, based on the solid information of the package manager based on the dependency tree not in any code that the library author might introduce there.

I’d like to explore this further. I’d agree that the config file generated by CMakeDeps has an advantage in knowing where the dependencies are located and potentially useful info about transitive dependency locations. However, I’d argue that the strongest config file is the one provided by the library itself, which lives in the package itself and where the library author knew exactly what to export/import. CMake recommends that all libraries export a config file. The next strongest is a config file provided by a recipe for that library, since the recipe author has to know details about the library anyway to fill out package_info. In both cases, authors can provide library-specific details that the generator can’t.

It is problematic that cmake best-practice strongly recommends authors provide their own config file yet conan (which is defacto cmake-focused) circumvents this practice with a more general replacement. Although I see benefit in the CMakeDeps generated one, I don’t think it’s acceptable to indiscriminately prevent the use of the specialized config files provided by the authors. At least, there should be ways to control the generator from the consumer side to elect one or the other.

Declaring the cmake_build_modules property will cause the CMakeDeps to include the file automatically when finding the config file generated by Conan. … Otherwise from the dependency recipe it should adjust self.cpp_info.set_property(“skip_deps_file”, True)

Given that pybind11 is a very well-done cmake library already and its current recipe in conan center is completely adequate, it is a design smell that I would need to fork one or the other in order to consume. I agree, it makes sense that the pybind11 recipe knows whether or not it exports is own config file and could indicate that, but it seems wrong to force the consumer one way or another. What is generated should be controlled at the consumer level.

You could remove the config file after calling CMakeDeps(self).generate() in your generate(self) method. Otherwise from the dependency recipe it should adjust self.cpp_info.set_property(“skip_deps_file”, True)

I have tried it both ways. Unfortunately these workarounds don’t work. In either case, I still have the issue that the path to the config file is not in the prefix path, like it would be if I were using conan_paths generator. I could try and force the CMAKE_PREFIX_PATH variable in the toolchain from the generate method, but there is no way to handle mutli-configuration conan installs from the generate method: the toolchain gets regenerated with each individual conan install, so I don’t have a way to do something like:

tc.variables.debug["CMAKE_PREFIX_PATH"] = <path to debug package>
tc.variables.release["CMAKE_PREFIX_PATH"] = <path to release package>

because conan install doesn’t know both <path to debug package> and <path to release package> in any single conan install invocation. CMakeDeps is supposed to be the way forward to handle cmake, including multi-configuration builds. Any suggestions on how to handle this?

0reactions
jwillikerscommented, Oct 12, 2021

@kenfred Yeah, it does sound like the visitor pattern would be a good paradigm for improving extensibility. A good test is probably whether support for Google Sanitizers can be used across a project and its dependencies without having to reach into dependencies’ recipes.

Read more comments on GitHub >

github_iconTop Results From Across the Web

CMakeDeps — conan 1.56.0 documentation
By default, Conan will include only the build modules from the host context (regular requires) to avoid the collision, but you can change...
Read more >
conan + cmake generators : include directories not found ...
#set CMAKE_MODULE_PATH to find cmake files generated by conan in build ... cmake generators than me, namely CMakeDeps and CMakeToolchain, ...
Read more >
conan Changelog - pyup.io
Feature: New extensions plugins to implement profile checking, package signing, ... Bugfix: Fix case where CMakeDeps assumes a module dependency when ...
Read more >
Why is it that package managers are unnecessarily hard?
It's mostly a language problem, Go has a very simple module system ... conan 2.0 is aiming to declare CMakeDeps and CMakeToolchain as...
Read more >
Software Architecture with C++
Writing modular C++. 160. Summary. 163. Questions. 164. Further reading. 164. Chapter 6: Design Patterns and C++.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found