Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] CMake generators - how to use libraries form a different build config #11607

Closed
1 task done
nolange opened this issue Jul 11, 2022 · 18 comments
Closed
1 task done
Assignees

Comments

@nolange
Copy link

nolange commented Jul 11, 2022

Hello,

I would want to fetch googletest via Conan, I am using v1.50 on debian Linux with Make/Ninja generators.
The problem is generally that you don't necessarily want the same config on your project as on your dependencies.
For my example I would want to build my project with debug/sanitize/coverage configuration, while gtest and other dependencies are the release variants.

I cant do this currently, using the CMakeDeps generator atleast. Doing a debug-build the find_package(GTest) call will work,
but those targets are empty and using gtest headers will fail.

Why isnt this done more like "normal" CMake installations where the property IMPORTED_CONFIGURATIONS is set, and CMake picks the correct flavor, while also allowing handpicked overrides via MAP_IMPORTED_CONFIG_<CONFIG>.

So in short, your generated imports should:

  • Drop all $<$<CONFIG:.*>:.*> stuff.
    otherwise its not possible to use them elsewhere.
  • use IMPORTED_CONFIGURATIONS instead.
  • check_build_type_defined shouldnt be necessary?

That way, conan packages could be used just like regular installed CMake packages when created with the official helpers.
Output of these helpers looks like this.

@nolange
Copy link
Author

nolange commented Jul 11, 2022

Seems to be a dupe of #8174 .
Still its quite a blocker for me if conan does not work beneath CMake and instead fundamentally changes how you have to build software, at least on non-windows.

@memsharded
Copy link
Member

The main problem is that Conan packages are single-configuration. You get each different binary, each different build configuration in a different location. But find_package() does not have any support for managing multiple locations, so we need to create the different CONFIG: stuff, and aggregate them.

You can define different settings for your dependencies than your current package, with conan install -s build_type=Release -s mypkg:build_type=Debug, I am not sure if this is what you want to achieve in the end, being able to consume Release dependencies while your current package is Debug.

@nolange
Copy link
Author

nolange commented Jul 11, 2022

I want to use gtest release build, while my package is whatever and actually not of conan's concern (not built with conan, just give me the deps).

What I am doing is this:

sed 's,$<CONFIG:[[:alnum:]]*>,1,g' -i ./build/generators/*-Target-*.cmake

and I would like to have an option for that.

I guess you have issues with multi-config buildsystems? cause with ninja/make you also only always get single-configuration, I dont see how the current way would add anything. Maybe make the logic (or the default option) conditional on that.

Btw. if you need multiple configurations, then maybe add subdirectories in build/generators? That way you still wont need the generator expressions, just have to decide which subdirectory to include.

@nolange
Copy link
Author

nolange commented Jul 11, 2022

Ill try to be more specific, as I am not sure where our misunderstanding lies.

cd /tmp/build
# install release build of gtest
conan install -e:h CONAN_CMAKE_TOOLCHAIN_FILE=$HOME/cmake/clang-toolchainfile.cmake -pr:b=default -pr:h llvm --build=outdated  -of . ~/git/project

# calls list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR}/build/generators"
# find_package(GTest REQUIRED)
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON --toolchain=$HOME/cmake/clang-toolchainfile.cmake ~/git/project

Note that without Conan, the CMake configure will work with every Build type, aslong any gtest library is installed. Normally you dont want to debug other software, but have it run normally ( = release build).

Now the lines above will execute, gtest will be found but add no include paths or libraries - which is kinda the worst thing since now everything but a couple tests will compile, then complains about missing gtest headers.

These builds of project wont be distributed, its a non-release build used during development and testing. Instead a row of configurations using sanitizers or code-coverage can be used - on those instrumentation you actively don't want the dependencies included.

I am using a separate build directory for each build_type, the generators are placed within, no conflicts there.

-s build_type=Release -s mypkg:build_type=Debug seems to affect the right knobs, but

  • mypkg:build_type=None adds $CONFIG:None, "None" is not a value that CMAKE_BUILD_TYPE is typically set to
    some setting. And some special value to remove the check would be welcome.
  • I would like a way to specify mypkg without naming it, like using :build_type=Debug.
  • ie. something like -s $project$:build_type=$Any$ could totally remove the checks on CONFIG and call to check_build_type_defined

@666melkor666
Copy link

Hi.
It looks like I'm hit by the same problem.

I need to be able to independently change the build_type of dependencies and their consumer.
The use case is the following: I have an application and want to use the release version of the dependencies regardless of the config my application is being built or change the build_type for a certain dependency only.
Using debug versions for all dependencies may lead to significant performance degradation.

The only workaround I've found is to override the generate method in the following way:

def generate(self):
    deps = CMakeDeps(self)
    deps.configuration = "Release"
    deps.generate()
    deps.configuration = "Debug"
    deps.generate()

and then conan install -s build_type="Release" ...
This way I have release version of dependencies, and I can change the consumer CMAKE_BUILD_TYPE between Release and Debug.
This approach, however, is very inconvenient, especially if I want to have some consumer specific CMAKE_BULD_TYPE, which for example, turns on a lot of additional logging.

It would be great to have the ability to tell the CMakeDeps generator to ignore the build_type setting.
In case I've missed something, please advise how I can build the described workflow using conan.

@kambala-decapitator
Copy link
Contributor

also stumbled on this, had a conversation on slack https://cpplang.slack.com/archives/C41CWV9HA/p1661246180811889

I experienced the following after having built deps in Release mode:

  • if config of deps and app matches, then all builds fine
  • when app config is RelWithDebInfo, then there's an error at app link step: https://pastebin.com/de3WqwVn
  • when app config is Debug, then include dirs aren't passed to compiler and build fails almost immediately

I am not sure if this is what you want to achieve in the end, being able to consume Release dependencies while your current package is Debug

exactly this, just like @666melkor666 writes:

I have an application and want to use the release version of the dependencies regardless of the config my application is being built

@kambala-decapitator
Copy link
Contributor

kambala-decapitator commented Aug 26, 2022

FYI I've just found that this actually works with the "legacy" cmake generators:

generators = "cmake_find_package", "cmake_paths"

Also just tried the workaround offered by @666melkor666 and it works!

@fanselm
Copy link

fanselm commented Sep 3, 2022

I also hit this recently, and I guess a lot of people must have by now.

I also agree with @nolange that the Conan generated .cmake files (e.g. <PKG>Config.cmake) should be completely free of configuration specifications like $CONFIG:Release - it is not Conan's job to interfere with how the project is configured, it should just hand you the include directories and libraries no matter what. If one wants a debug version of a dependency that is specified in the conanfile.txt.

@memsharded can you comment on this?

feltech added a commit to feltech/OpenAssetIO that referenced this issue Nov 23, 2022
Whilst attempting to write a test executable with an embedded Python
interpreter for OpenAssetIO#739, it turned out that the old problem reported in
OpenAssetIO#528 was still present.

To recap, ultimately (via circuitous logic) every CMake target created
by the Conan pybind11 package links to `libpython` (the
`pybind11::embed` target is the only one that should). This means that
Python extension modules (`pybind11::module`s) link to `libpython`,
causing linker errors at runtime or even segfaults due to ODR
violations.

As of conan-io/conan-center-index#13283 this bug
was fixed upstream. However, we still see it because the bug was not
fixed for the deprecated `cmake_find_package` generator, which we use.

It has not affected us so far because we haven't added in the
`Development.Embed` component to our `find_package(Python` call. As soon
as we add that component (e.g. for adding the aforementioned Python unit
test) we see this problem.

So modernise our `conanfile.py` to use the non-deprecated `CMakeDeps`
generator.

`CMakeDeps` insists on having the `build_type` setting available, so add
that.

Unfortunately `CMakeDeps` is designed to install dependencies with the
same configuration (Release, Debug, etc) as the parent project, which is
usually undesirable - we want Release versions of dependencies whilst
we develop the project in Debug mode. This is logged upstream in
conan-io/conan#11607, where a workaround is
posted, which is used here (see code comments).

Signed-off-by: David Feltell <david.feltell@foundry.com>
feltech added a commit to feltech/OpenAssetIO that referenced this issue Nov 23, 2022
Whilst attempting to write a test executable with an embedded Python
interpreter for OpenAssetIO#739, it turned out that the old problem reported in
OpenAssetIO#528 was still present.

To recap, ultimately (via circuitous logic) every CMake target created
by the Conan pybind11 package links to `libpython` (the
`pybind11::embed` target is the only one that should). This means that
Python extension modules (`pybind11::module`s) link to `libpython`,
causing linker errors at runtime or even segfaults due to ODR
violations.

As of conan-io/conan-center-index#13283 this bug
was fixed upstream. However, we still see it because the bug was not
fixed for the deprecated `cmake_find_package` generator, which we use.

It has not affected us so far because we haven't added in the
`Development.Embed` component to our `find_package(Python` call. As soon
as we add that component (e.g. for adding the aforementioned Python unit
test) we see this problem.

So modernise our `conanfile.py` to use the non-deprecated `CMakeDeps`
generator.

`CMakeDeps` insists on having the `build_type` setting available, so add
that.

Unfortunately `CMakeDeps` is designed to install dependencies with the
same configuration (Release, Debug, etc) as the parent project, which is
usually undesirable - we want Release versions of dependencies whilst
we develop the project in Debug mode. This is logged upstream in
conan-io/conan#11607, where a workaround is
posted, which is used here (see code comments).

Signed-off-by: David Feltell <david.feltell@foundry.com>
feltech added a commit to feltech/OpenAssetIO that referenced this issue Nov 25, 2022
Whilst attempting to write a test executable with an embedded Python
interpreter for OpenAssetIO#739, it turned out that the old problem reported in
OpenAssetIO#528 was still present.

To recap, ultimately (via circuitous logic) every CMake target created
by the Conan pybind11 package links to `libpython` (the
`pybind11::embed` target is the only one that should). This means that
Python extension modules (`pybind11::module`s) link to `libpython`,
causing linker errors at runtime or even segfaults due to ODR
violations.

As of conan-io/conan-center-index#13283 this bug
was fixed upstream. However, we still see it because the bug was not
fixed for the deprecated `cmake_find_package` generator, which we use.

It has not affected us so far because we haven't added in the
`Development.Embed` component to our `find_package(Python` call. As soon
as we add that component (e.g. for adding the aforementioned Python unit
test) we see this problem.

So modernise our `conanfile.py` to use the non-deprecated `CMakeDeps`
generator.

`CMakeDeps` insists on having the `build_type` setting available, so add
that.

Unfortunately `CMakeDeps` is designed to install dependencies with the
same configuration (Release, Debug, etc) as the parent project, which is
usually undesirable - we want Release versions of dependencies whilst
we develop the project in Debug mode. This is logged upstream in
conan-io/conan#11607, where a workaround is
posted, which is used here (see code comments).

Signed-off-by: David Feltell <david.feltell@foundry.com>
feltech added a commit to feltech/OpenAssetIO that referenced this issue Nov 28, 2022
Whilst attempting to write a test executable with an embedded Python
interpreter for OpenAssetIO#739, it turned out that the old problem reported in
OpenAssetIO#528 was still present.

To recap, ultimately (via circuitous logic) every CMake target created
by the Conan pybind11 package links to `libpython` (the
`pybind11::embed` target is the only one that should). This means that
Python extension modules (`pybind11::module`s) link to `libpython`,
causing linker errors at runtime or even segfaults due to ODR
violations.

As of conan-io/conan-center-index#13283 this bug
was fixed upstream. However, we still see it because the bug was not
fixed for the deprecated `cmake_find_package` generator, which we use.

It has not affected us so far because we haven't added in the
`Development.Embed` component to our `find_package(Python` call. As soon
as we add that component (e.g. for adding the aforementioned Python unit
test) we see this problem.

So modernise our `conanfile.py` to use the non-deprecated `CMakeDeps`
generator.

`CMakeDeps` insists on having the `build_type` setting available, so add
that.

Unfortunately `CMakeDeps` is designed to install dependencies with the
same configuration (Release, Debug, etc) as the parent project, which is
usually undesirable - we want Release versions of dependencies whilst
we develop the project in Debug mode. This is logged upstream in
conan-io/conan#11607, where a workaround is
posted, which is used here (see code comments).

Signed-off-by: David Feltell <david.feltell@foundry.com>
@dariusarnold
Copy link

dariusarnold commented Feb 25, 2023

Hey, I also stumbled onto this issue today while trying out Conan 2.0 for the first time. At first I thought this was a bug in the new version until I looked at the INTERFACE_INCLUDE_DIRECTORIES property of the target created by Conan and saw the generator expression contain a build type different from the one I was using. I was quite perplexed by this, especially since previously in Conan 1.X I was using cmake_find_package generator where, as @kambala-decapitator said, this would work.
Is there any plan for a more general solution apart from the current options?

  • Specifying a different build type for every dependency in the install command
  • Generate twice with different build types

There is also another discussion, which suggests two additional solutions:
#12656

@bigcat26
Copy link

bigcat26 commented Apr 4, 2023

I'm facing the exact same issue as when I migrated from Conan 1.x to 2.x today. I need a prebuilt package of gtest, while my own project is built with the debug configuration.

@memsharded
Copy link
Member

Just to emphasize the above message. Using libraries built with a different build_type than the current project is perfectly possible, just Conan needs to be aware of it, for example, if our current project is Debug, but we want the dependencies in Release mode, it can be done with:

conan install . -s build_type=Release -s &:build_type=Debug
cmake ... -DCMAKE_BUILD_TYPE=Debug

For users that don't understand the need for this, let me put this simple example, I have found it several times in production code at some users codebase:

def requirements(self):
      if self.settings.build_type == "Debug":
           self.requires("mylib/1.1")
     else:
           self.requires("mylib/2.0")

Lets say that for some reason, the debug usage of mylib is still not there for 2.0, so the conditional was added to the consumer package recipe.
Lets assume for simplicity that mylib is a header only library (but the reasoning extrapolates to all kinds of libraries, and any conditional on the shared option, on other settings, etc), and now we do:

conan install . -s build_type=Release
conan install . -s build_type=Debug

The mylib doesn't have any settings defined, because it is header-only. Still Conan needs to generate 2 files:

  • mylib-release-config.cmake (pointing to the artifacts of mylib/2.0)
  • mylib-debug-config.cmake (pointing to the artifacts of mylib/1.1)

So the configuration of the generated files is depending on the consumer configuration, not on the dependencies configuration, as clearly evidenced by this header-only package.

And this is the reason that Conan needs to be aware of the build_type of the dependencies and the build_type of the current project, and the recommended approach to do this is:

conan install . -s build_type=Release -s &:build_type=Debug
# It can also be explicit with ``-s consumer_name/*:build_type=Debug``

That way, conan packages could be used just like regular installed CMake packages when created with the official helpers.
Output of these helpers looks like this.

As described above, Conan package management do more than regular CMake, and allow defining dynamic and variable dependency graphs based on multiple factors, including the build_type. And this is why Conan needs to be informed about the build_types, and relying only on the behavior of IMPORTED CMake approach would not be enough to allow this functionality.

I am closing this question as responded. If the conan install . -s build_type=Release -s &:build_type=Debug approach is not working, please let us know creating a new ticket. Thanks!

@nolange
Copy link
Author

nolange commented Jul 4, 2023

That way, conan packages could be used just like regular installed CMake packages when created with the official helpers.
Output of these helpers looks like this.

As described above, Conan package management do more than regular CMake, and allow defining dynamic and variable dependency graphs based on multiple factors, including the build_type. And this is why Conan needs to be informed about the build_types, and relying only on the behavior of IMPORTED CMake approach would not be enough to allow this functionality.

And no one ever argued about that. But there's a live outside the finally packaged product where we develop our stuff and often prefer an existing or specially crafted dependency (like built with sanitizers).

I am closing this question as responded. If the conan install . -s build_type=Release -s &:build_type=Debug approach is not working, please let us know creating a new ticket. Thanks!

This is still more tinkering then I would like, but might suffice.

@memsharded
Copy link
Member

This is still more tinkering then I would like, but might suffice.

Yeah, we would also love to have a simpler interface. But it is always a trade-off, when more and more complex cases need to be managed, then, it becomes necessary to be more explicit about the intention on the interface.

We have tried in the past to be "smarter", and it is very frequently inevitably a source of problems and frustration, as it is not possible to be smart enough to cover all cases, and it becomes very easy to generate blockers for quite a few users. So the current Conan design generally requires being very explicit in many situations, because the alternatives were worse overall for the wider community (it is possible that it might be better for some users, but if it destroys the usage for a few others, the net is still quite negative)

@feltech
Copy link

feltech commented Aug 10, 2023

We're currently using the solution in #11607 (comment) for local development.

Recently though I came across CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>, which may be downstream way of adapting build types between project and dependencies. Haven't tried it in anger yet, though.

@kambala-decapitator
Copy link
Contributor

conan install . -s build_type=Release -s &:build_type=Debug

finally an out-of-box solution, thanks!

however, I'm having troubles finding where this special & operator is documented in the online docs. @memsharded could you point?

and do I understand correctly that it's available only in 2.x?

@dariusarnold
Copy link

@kambala-decapitator
It is documented in the install command: https://docs.conan.io/en/1.60/reference/commands/consumer/install.html and also in requirements: https://docs.conan.io/1/devtools/build_requires.html

It is available in recent 1.X versions as well as 2.X.

@memsharded
Copy link
Member

The & is also documented in "Profile patterns" section in Conan 2.0: https://docs.conan.io/2/reference/config_files/profiles.html#profile-patterns

@kambala-decapitator
Copy link
Contributor

@kambala-decapitator It is documented in the install command: https://docs.conan.io/en/1.60/reference/commands/consumer/install.html and also in requirements: https://docs.conan.io/1/devtools/build_requires.html

It is available in recent 1.X versions as well as 2.X.

thanks! Funny that conan install page in 2.x doesn't have it (where I looked at and expected to find)

The & is also documented in "Profile patterns" section in Conan 2.0: https://docs.conan.io/2/reference/config_files/profiles.html#profile-patterns

thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants