conan
conan copied to clipboard
[question] Is there a way to create a conan package that combines both Release and Debug in the same package?
What is your question?
Is there a way to create a conan package that combines both Release and Debug in the same package?
Conan's package methodology of one build_type (Release, Debug, RelWithDebugInfo, MinSize) per package, breaks CMake's methodology of many configurations may be in a single CMake relocatable package.
This is problematic when we use the relocatable CMake package files that we generate for ourselves and our customers, or when using the CMake package files that 3rd party open source library's provide in combination with Visual Studio's multi-configuration IDE.
I know about the suggested method of
conan install . -s build_type=Release
conan install . -s build_type=Debug
<cmake commands next>
works for Windows Visual Studio multi-configuration when using the conan generated CMake files. However, it does not work when using standard relocatable CMake packages.
I did some digging into this and found that this appears to be a fundamental mismatch between conan and CMake packages.
When we create a relocatable CMake package for release and debug images it is typically done with a folder hierarchy such as:
<foo-package-root>/
+--cmake/
| +--foo-config-debug.cmake
| +--foo-config-release.cmake
| +--foo-config.cmake
|
+--include/
|
+--lib/
+--fooD.lib
+--fooR.lib
To find the foo
package it would either be installed to a common root location, or the CMAKE_MODULE_PATH
value is updated to include the path to <foo-package-root>
.
When find_package(foo...)
is invoked it finds this location and has the ability to return one of the two configurations listed.
If we tried this in the conan world where we have one configuration per package we end up with two separate paths/repos where each one has just one configuration. Such as the following:
<conan-debug-path>
| +--cmake/
| | +--foo-config-debug.cmake
| | +--foo-config.cmake
| |
| +--include/
| |
| +--lib/
| +--fooD.lib
<conan-release-path>
+--cmake/
| +--foo-config-release.cmake
| +--foo-config.cmake
|
+--include/
|
+--lib/
+--fooR.lib
This mechanism can never work for a Visual Studio multi-configuration situation because cmakes find_package(foo...)
stops when it finds the first cmake package with the correct name.
If CMAKE_MODULE_PATH
has <conan-debug-path>
listed first then it will stop when it finds <conan-debug-path>/cmake/foo-config.cmake
and will always and only return the debug version of the library.
This is because it doesn't know about the existence or location of the release library.
Why don't we just use the conan generated CMake files?
We don't use the conan generated CMake files because conan doesn't know about any extra logic or variables that we or 3rd party libraries place in our relocatable CMake package files.
Workaround
I've found and confirmed the following workaround.
-
Start with a leaf level project that doesn't have any requirements
-
Use the conan way to start a multi configuration toolchain.
conan install . -s build_type=Release conan install . -s build_type=Debug
-
Generate the cmake build files
cmake . -B build -A x64 ^ -DCMAKE_TOOLCHAIN_FILE="<path to toolchain file" ^ -DCMAKE_POLICY_DEFAULT_CMP0091=NEW
-
Compile R and D and install to the local INSTALL folder
cmake --build build --config Release cmake --install build --config Release --prefix "%CD%\INSTALL" cmake --build build --config Debug cmake --install build --config Debug --prefix "%CD%\INSTALL"
After this point the INSTALL folder has both R and D libraries as well as the relocatable package to work for R and D libraries.
-
Set the
conanfile.py
logic to package the contents of the INSTALL folder and to use the relocatable CMake package logic. -
Export the package
conan export-pkg . -s build_type=Release conan export-pkg . -s build_type=Debug
At this point we have two conan packages for Release and Debug in the local cache, where each package is a duplicate of the other with both Release and Debug libraries and cmake logic.
When consumers of this package try to set up multi configuration following the conan way
conan install . -s build_type=Release
conan install . -s build_type=Debug
The local toolchain is set up for Release and Debug and the package folders that are searched will be able to provide both Debug and Release images.
Is there a better conan way to do this?
I've confirmed that this workaround works. But it feels hacky. I don't like it. Is there an better way to do this? A conan way to do this?
Have you read the CONTRIBUTING guide?
- [X] I've read the CONTRIBUTING guide
Hi @RochaStratovan
Thanks for your question.
In short, no, the Conan model is for 1 binary configuration per-package, for multiple reasons:
- It is the only approach that really scales. For example many users also manage other configs like RelWithDebInfo. Adding all possible configs inside a package is not possible.
- It is important that packages are immutable. That means that both the Release and Debug configurations must be built at the same time, which is very inefficient in many cases
- Packaging Debug artifacts together with Release ones is even considered a security risk, with risk of leaking the wrong files to production or to users that would have Debug information unveiling proprietary IP
- Debug artifacts are large. This makes this approach very inefficient for many cases that really only need to build the production (Release) version
The CMake is really a build-model not a package-model. Having real binary packages that package more than 1 config inside the same package does not really works in most real world package management scenarios. We know that there are compromises that should be made, like the issue with modules embedded in packages, but there could be other approaches to that, depending on the functionality in those modules. In many cases there could be extracted to a separate tool_require
packages, the idea is that packages should be build-system agnostic, if you compile a package, that package should be usable in Meson or MSBuild too, not only CMake.
Could you please elaborate a bit more how your files
+--cmake/
| +--foo-config-debug.cmake
| +--foo-config-release.cmake
| +--foo-config.cmake
look like?
Wouldn't it be possible to have the foo-config-debug.cmake
in the debug package, the foo-config-release.cmake
in the release package and the foo-config.cmake
would be just a simple wrapper that could be generated in the consumer side? Or there could have something injected from the consumer side that would help locating the other binary in the other cache folder?
Wouldn't it be possible to have the foo-config-debug.cmake in the debug package, the foo-config-release.cmake in the release package and the foo-config.cmake would be just a simple wrapper that could be generated in the consumer side? Or there could have something injected from the consumer side that would help locating the other binary in the other cache folder?
I don't know. But my first reaction is I don't think so. These files are generated by the provider, not the consumer.
What do the config files look like?
The files we're using are autogenerated using CMake Relocatable Packages. There have been times that we've hand crafted similar file sets when we wanted more than CMake's basic files, but the core usage is the same. I've seen 3rd party libraries use both scenarios.
The CMake documentation for this is a pretty quick read.
The section Creating Relocatable Packages under their Importing and Exporting Guide is the basis for how we generate most of our packages.
I've created a simple example github repository for this conan-cmake-relocatable-package.
Disclaimer: I am comfortable with CMake, and really new to conan. I've taken some short cuts to get this simple example working as easily as possible.
The example defines a library that provides a function simple()
.
The simple()
function prints the following for a Debug build
This is DEBUG mode
and
This is NOT-DEBUG mode.
otherwise.
These are the steps to view the auto generated relocatable CMake Package files
These commands are done on a Windows machine with VS2022 installed and I'm using git bash
because I prefer the Linux CLI.
# Ensure the workspace is clean
git clean -xfd .
# Use cmake to generate the 64 bit VS2022 VS compile files
cmake . -B build -G "Visual Studio 17 2022" -A x64
# build the release binaries
cmake --build build --config Release
# build the debug binaries
cmake --build build --config Debug
# install the release binaries + cmake files to my local INSTALL
# folder
cmake --install build --prefix "${PWD}/INSTALL" --config Release
# install the debug binaries + cmake files to my local INSTALL
# folder
cmake --install build --prefix "${PWD}/INSTALL" --config Debug
I've also wrapped all of this in conan. You may have lots of WTF did you do that for moments. Remember, I'm just starting with conan.
After doing the above you can test this using the conan test_pkg using
conan export-pkg . -s build_type=Release
conan export-pkg . -s build_type=Debug
TL;DR No, not officially
This isn't officially possible, but the method describe above could be done but it breaks conan rules since a conan package typically only has one binary configuration in it.
Thanks for following up @RochaStratovan and sorry I didn't respond back then.
Yes, Conan might support multiple configurations inside a package, but most build-system integrations are designed to handle 1 configuration, so doing that might require quite some effort replacing or customizing different parts of the toolchains and build system integrations.