-
Notifications
You must be signed in to change notification settings - Fork 954
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
[feature request] Support encapsulation of the dependencies' headers #4038
Comments
(IMHO) I think that something like this should be taken into account for v2.0 (and backported to v1.0 if it is possible without breaking anything), and all of this mixed with the DLL Hell too... 😅 |
I'm adding this issue to the milestone 2.0, we won't have time to think about it for a 1.x release, but we will want to consider it for 2.0 for sure. |
Here is my workaround for this issue. It treats all existing dependencies in # extract all direct dependencies from conaninfo.txt
file( STRINGS "${CMAKE_BINARY_DIR}/conaninfo.txt" conan_info )
set( direct_conan_dependencies )
set( in_requires_block OFF )
foreach( line ${conan_info} )
if ( ${line} STREQUAL "[requires]" )
set( in_requires_block ON )
elseif ( in_requires_block )
if ( ${line} MATCHES "\\[[a-z]+\\]" )
break()
else()
string( FIND "${line}" "/" pkg_separator_pos )
string( SUBSTRING "${line}" 0 ${pkg_separator_pos} pkg_name )
string( STRIP "${pkg_name}" pkg_name )
list( APPEND direct_conan_dependencies "CONAN_PKG::${pkg_name}" )
endif()
endif()
endforeach()
message( STATUS "Direct dependencies: ${direct_conan_dependencies}" )
foreach( conan_target ${CONAN_TARGETS} ) # CONAN_TARGETS is defined in conanbuildinfo.cmake, generated by conan
list( FIND direct_conan_dependencies ${conan_target} direct_deps_pos )
if ( ${direct_deps_pos} EQUAL -1 ) # not a direct dependency
# remove all include directories of transitive dependency
set_property( TARGET ${conan_target} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "" )
endif()
endforeach() Basically, it parses This then forces the developer to define all their dependencies in their conanfile. This gets annoying if public headers in NOTE: Adding |
Implemented and released in 2.0-alpha |
Let's consider the following scenario:
Aaa
contains some utilitiesBbb
uses packageAaa
, but it's public API headers do not depend in any way on headers fromAaa
Ccc
uses packageBbb
, however, a developer ofCcc
package accidentally included headers fromAaa
and used functions from it, without addingAaa
to therequires
attribute of theCcc's
conanfile.py. This worked correctly for the developer of theCcc
package but will cause major problems to the users of theCcc
package, as explained below.Ddd
package) uses onlyCcc
package, but at some point decides to overrideAaa
to v2.0.0. SinceBbb
depends onAaa
, Conan rebuilds theBbb
but it does not rebuildCcc
, as Conan does not know thatCcc
usesAaa
internally. This causes a linker error to the developer of theDdd
package (or further downstream ifDdd
is another static lib) in the best scenario, or even worse, it causes the misbehaving application that is almost impossible to debug.The solution to this problem would be to Conan not exposing
Aaa's
headers to theCcc
package unless there is a direct dependency on it.I tried setting
Aaa
asprivate
dependency of theBbb
inthis example, however, although this resulted with the desired behaviour in
Ccc
package, it caused a linker error inDdd
package, asAaa's
binaries were not linked in.The correct behaviour would be if Conan generated the build system in a way that when some package is a direct dependency, the build system's target generated for that package would contain information about include paths, preprocessor definitions, compile flags, link flags and link libraries, but when some package is an indirect (transitive) dependency, the build system's target generated for that package would contain only link flags and link libraries, but no compile information, such as preprocessor definitions, compile flags and include paths.
In this case, when compiling
Ccc
that includesAaa's
headers, you will get a compile error, unless you state thatCcc
depends onAaa
in itsrequires
list. However, when usingCcc
fromDdd
or further downstream, you will not get a linker error, asAaa's
binaries will get linked into the final binary.But there is a catch...
However, sometimes some libraries have public headers that depend on public headers of another library. In that case, you want the current behaviour, except that breaking change of that library should trigger a rebuild of all libraries that see its headers. For example, if
Bbb's
public headers include public headers fromAaa
, then overridingAaa
fromDdd
should also trigger the rebuild ofCcc
, as it usesBbb's
public header that depends onAaa
.To make this mess less complicated and much easier to understand, I suggest introducing additional parameters to the
requires
attribute. Currently, it supports only two parameters:override
andprivate
and I suggest addingapi
andimplementation
parameters (the idea actually comes from gradle - I really suggest reading this link).If
Bbb
states that it requiresAaa
for its implementation (which I argue should be the default when nothing is specified), something like this:Then,
Ccc
will get a compile error if it tries to include headers fromAaa
(unless it adds direct dependency onAaa
), butDdd
will still link correctly, unlike in the case whenBbb's
dependency onAaa
isprivate
(private
is most similar to gradle'scompileOnly
).However, if
Bbb
states that it requiresAaa
also for its API, something like this:Then,
Ccc
will be able to include headers, compile flags and preprocessor definitions fromAaa
, even though it does not have a direct dependency onAaa
. However, the Conan will then know that ifDdd
or someone further downstream overridesAaa
with a breaking version, thatCcc
also needs to be rebuilt, i.e. it's package ID will have to depend onAaa
.Current behaviour treats package ID as in
implementation
mode, and include path propagation as inapi
mode, thus making a very hard time for code reviewers to manually detect cases when a developer included a header that is either not from a direct dependency or includes a header from a transitive dependency.I argue that this feature is a must for Conan v2.0, but let's discuss whether it may be possible to add this even to v1.x. Stating that either
implementation
orapi
mode is the default will definitely break existing packages:api
would cause different package IDs to be generated for current dependency chains, which would cause massive rebuilds of everything, which is very bad andimplementation
would break packages that were broken in the first place because they used headers from transitive dependencies.To make things backward compatible I suggest adding a
legacy
parameter to therequires
attribute which would be the default and would have the exact same behaviour as Conan currently has, except that it will print out a warning that it should not be used due to problems mentioned here (similar as you have done with transition ofdefault_options
from being a tuple to being a dictionary). Then, in v2.0 you can simply remove thelegacy
parameter and set theimplementation
to be the default.The text was updated successfully, but these errors were encountered: