[question] Express Transitive Components from package_info
What is your question?
Hi,
I have something like the following requirements for package "MyPackage":
# from package MyPackage
def requirements(self):
self.requires("a/1.0")
self.requires("b/2.0")
self.requires("c/3.0")
a, b and c are shared libs packages that creates several libs, and export components information regards them at package info methods, assume:
# from package a / b / c
def package_info(self):
self.cpp_info.components["x"].libdirs = xlibdirs
self.cpp_info.components["x"].includedirs = xincludedirs
self.cpp_info.components["x"].bindirs = xbindirs
self.cpp_info.components["x"].libs = ["x"]
self.cpp_info.components["y"].libdirs = ylibdirs
self.cpp_info.components["y"].includedirs = yincludedirs
self.cpp_info.components["y"].bindirs = ybindirs
self.cpp_info.components["y"].libs = ["y"]
now, MyPackage is a single lib shared library package, which includes c headers at it's own public headers, which means MyPackage consumers should get the transitive c headers propagated from MyPackage, so according to documentation I should do something like:
# from package MyPackage
def package_info(self):
self.cpp_info.requires = ["c::y"]
but that fails on package method (MyPackage/x.0.0.0: Calling package()) with the following:
ERROR: MyPackage/x.0.0.0: Required package 'a' not in component 'requires'.
well, of course some of 'a' components are required, I just don't want them to get to the downstream. so can I state those are private implementation details ? how can I express the true transitivity of my package ?
Have you read the CONTRIBUTING guide?
- [ ] I've read the CONTRIBUTING guide
Hi @mikirothman
Thanks for your question
Required package 'a' not in component 'requires'.
I have a quick question. If a is a shared library, and it is a requires, there is no way to hide it from the consumers. The consumers of mypackage will necessarily need the a shared libraries to run if you are requiring a, it cannot be a "private implementation detail", am I missing something?
In any case, if something is really a private implementation detail, and you want to completely hide it, it is possible to define visible=False trait. In the majority of cases it will not be necessary, Conan can avoid the overlinking in the consumers of mypackage, it will only link the transitive dependencies when necessary.
Yeah, that is correct. the fact that the error occurred only when using require for some of the deps was pretty weird and still I can't understand why it didn't produce the error when not requiring any of the deps.
regarding the visibility trait, as you said, I can't think of a case it is necessary, but thanks anyway for the awesome work and assistance
the fact that the error occurred only when using require for some of the deps was pretty weird and still I can't understand why it didn't produce the error when not requiring any of the deps.
When no components are require, it implicitly depend on the "root" components, that is the pkg::pkg of each dependency defined in requires. When components are detailed, then it does no longer add that default dependency to the package, as it expects a user-defined one of the components it requires, and raises an error if it detects something is not used at all, because it would be something missing in the definition.
Thanks ! that makes sense. now, assume MyPackage is a single lib package (no components exposed), which indicates it's requires (specific components from a, b and c), it still seems that when consuming MyPackage, the entire components of a, b and c are linked with the consumer, is that the correct behavior ? should I create a component just to avoid linking the entire dependency graph ?
is that the correct behavior ? should I create a component just to avoid linking the entire dependency graph ?
Are you sure this is the case? I have checked one of the tests in the suite, and it seems to be doing the right thing: https://github.com/conan-io/conan/blob/2e568138707a4d35a53e063a86af2aa6ee2793fb/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_components.py#L9
The top package contains top::cmp1 and top::cmp2. As long as the intermediate package does a self.cpp_info.requires = ["top::cmp1"], only top::cmp1 is linked in the consume application, but not top::cmp2.
OK, so assume the following example:
from package MyPackage
def requirements(self): self.requires("a/1.0", transitive_headers=True, transitive_libs=True) self.requires("b/2.0", transitive_headers=True, transitive_libs=True) self.requires("c/3.0")
def package_info(self): self.cpp_info.components["x"].requires = ["a::a", "b::b", "c::c"] self.cpp_info.components["y"].requires = ["a::a", "b::b", "c::c"]
here we have a package that creates 2 libraries with 3 requirements:
- a - public dependency
- b - public dependency
- c - private dependency
now lets assume that component x exposing a::a and b::b as public dependencies and component y exposing only a::a as public dependency.
the consumer of component y will still get b::b propagated as a public dependency, there is no option to declare it private from the component side. the private / public characteristic of the dependency is resolved by the entire package.
users of component y would be able to include headers from b::b even when they are not supposed to.
what do you think? does it make sense or am I missing something ?