conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.3k stars 986 forks source link

[feature] conan v2: Provide a config so that all direct requires behave as `force=True` implicitly #13201

Open SpaceIm opened 1 year ago

SpaceIm commented 1 year ago

What is your suggestion?

In conan v2, versions conflicts are not resolved automatically. Even a direct dependency of top conanfile does not override transitive dependency version if force=True is not explicitly set in conanfile.py (and it's impossible in conanfile.txt or command line). While this behavior might be desirable in companies with well written conanfile.py, lockfile etc, I don't think it's appropriate for new comers or simple projects with a conanfile.txt, or while testing a recipe with conan create, because versions conflicts will always happen in sufficiently complex dependency graphs. conan-center has never been able to provide a global dependency graph without versions conflicts between transitive dependencies during the past 3 years (just a new version of zlib or openssl takes months to be propagated to all conan-center recipes), so how could it scale?

Therefore I think a config changing the behavior as if all direct dependencies had force=True (recursively) would be convenient.

I'm pretty sure that conan-center will be beaten by this graph model very quickly in recipes with many direct & transitive dependencies, like arrow, cairo, cpython, drogon, ffmpeg, folly, gdal, grpc, itk, opencv, qt etc.

Have you read the CONTRIBUTING guide?

memsharded commented 1 year ago

Hi @SpaceIm

When we changed this for Conan 2.0 is because we had consistent feedback that the default of overriding without even noticing was not a good one, and more and more users were using CONAN_ERROR_ON_OVERRIDE.

Having a conf that changes back this behavior might not be a good approach for ConanCenter:

So the way to move forward, both for ConanCenter and for enterprise users with their own recipes is similar:

It is important to recall that the current state of ConanCenter is often problematic. I have talked to a bunch of enterprise users that have forks of conan-center-index and basically the major divergence from upstream is making all versions converge to avoid the conflicts that otherwise regularly happen when using multiple dependencies (companies using 50-80 recipes from ConanCenter is not unusual). It is important that we start to proactively address this with infrastructural solutions

SpaceIm commented 1 year ago

Version range in conan-center would be fantastic, but again I'm not sure how it could scale since it would quickly lead to missing package id (nightly build may help but I don't know if c3i infrastructure is ready).

Whatever solution is chosen, from a user perspective I think that being able to easily solve versions conflicts is important. And user experience of new comers shouldn't be underestimated.

db4 commented 1 year ago

@SpaceIm thanks for rising this issue. IMHO, ConanCenter is quickly becoming unmanageable. That's why enterprise users are forced to create their own forks to achieve minimal stability. Even updating existing recipes for Conan v2 is problematic: say, I need to make pango V2 compatible. It depends on glib directly and indirectly via 2 other packages. Accepting PRs in CCI takes ages nowadays. While I'm trying to "bump dependencies" of some package, someone else is doing the same, and oops... welcome a version conflict! To illustrate the problem, here is a list of glib versions that various CCI recipes currently require:

./aravis/all/conanfile.py:        self.requires("glib/2.75.2")
./aravis/all/conanfile.py:            self.tool_requires("glib/2.75.2")
./at-spi2-atk/all/conanfile.py:        self.requires("glib/2.73.0")
./at-spi2-core/all/conanfile.py:        self.requires("glib/2.73.0")
./at-spi2-core/new/conanfile.py:        self.requires("glib/2.75.2")
./atk/all/conanfile.py:        self.requires("glib/2.75.2")
./atk/all/conanfile.py:            self.tool_requires("glib/2.75.2")
./avahi/all/conanfile.py:        self.requires("glib/2.75.2")
./avahi/all/conanfile.py:        self.tool_requires("glib/2.75.2")
./cairo/all/conanfile.py:            self.requires("glib/2.76.1")
./cairo/meson/conanfile.py:            self.requires("glib/2.76.1")
./dbus/1.x.x/conanfile.py:            self.requires("glib/2.76.0")
./enchant/all/conanfile.py:    requires = "glib/2.71.3", "hunspell/1.7.0"
./gdk-pixbuf/all/conanfile.py:        self.requires("glib/2.76.0", transitive_headers=True, transitive_libs=True, run=can_run(self))
./gdk-pixbuf/all/conanfile.py:            self.tool_requires("glib/2.76.0")
./glibmm/all/conanfile.py:        self.requires("glib/2.76.0")
./gobject-introspection/all/conanfile.py:        self.requires("glib/2.73.0")
./graphene/all/conanfile.py:            self.requires("glib/2.75.2")
./gst-libav/all/conanfile.py:        self.requires("glib/2.70.1")
./gst-plugins-bad/all/conanfile.py:        self.requires("glib/2.70.1")
./gst-plugins-base/all/conanfile.py:        self.requires("glib/2.72.0")
./gst-plugins-good/all/conanfile.py:        self.requires("glib/2.70.0")
./gst-plugins-ugly/all/conanfile.py:        self.requires("glib/2.70.1")
./gstreamer/all/conanfile.py:        self.requires("glib/2.72.0")
./gtk/all/conanfile.py:        self.requires("glib/2.73.0")
./harfbuzz/all/conanfile.py:            self.requires("glib/2.76.0", run=can_run(self))
./harfbuzz/all/conanfile.py:            self.tool_requires("glib/2.76.0")
./libsecret/all/conanfile.py:        self.requires("glib/2.70.1")
./libverto/all/conanfile.py:            self.requires("glib/2.76.0")
./libvips/all/conanfile.py:        self.requires("glib/2.76.1", transitive_headers=True, transitive_libs=True, run=can_run(self))
./libvips/all/conanfile.py:            self.tool_requires("glib/2.76.1")
./pango/all/conanfile.py:        self.requires("glib/2.73.3")
./pangomm/all/conanfile.py:        self.requires("glib/2.72.1")
./poppler/all/conanfile.py:            self.requires("glib/2.73.2")
./pulseaudio/all/conanfile.py:            self.requires("glib/2.76.0")
./qt/5.x.x/conanfile.py:            self.tool_requires("glib/2.76.1")
./qt/5.x.x/conanfile.py:            self.requires("glib/2.76.1")
./qt/6.x.x/conanfile.py:            self.requires("glib/2.76.1")
./qxmpp/all/conanfile.py:            self.requires("glib/2.70.1")

I would call it a nightmare. And glib is just one example.

If we talk of other package managers, they usually have stable and unstable distribution. I think many users would like to have something similar from ConanCenter. The stable distribution contains not the most recent versions but the global graph is always conflict-free. The question is how to achieve that. Maybe introduce server-side lock files that would define a stable set of recipe revisions (i.e. repository version)? And a set of tools to manage them (add a recipe to a given version etc.). Then if a client selects a specific repository version, other recipe revisions become invisible (for conan search, install etc.)

I don't insist on that, maybe there are better ideas, but IMHO the current CCI situation is hardly acceptable and requires quick actions.