Hi!

On Mon, 2018-06-18 at 19:55:31 -0700, Ximin Luo wrote:
> Package: dpkg
> Version: 1.19.0.5+b1
> Severity: wishlist

We discussed this briefly yesterday on IRC before Ximin filed the
report. I don't see much difference with what was mentioned and I'll
repeat what David Kalnischkies and me proposed on the spot, as a
potentiall working solution.

> The Rust package manager Cargo, makes it possible to declare two-sided version
> constraint ranges such as X (>= 6, << 7). In dpkg, historically this has been
> expressed by declaring two one-sided version constraint ranges for the same
> package like X (>= 6), X (<< 7), however this is not sufficient for Cargo due
> to its other feature of allowing transitive dependencies on multiple versions
> of the same package.
> 
> Currently to support this we are creating packages called X-$version, and
> declaring Provides: X (= $version). This breaks in the following situation:
> 
> mdbook 0.1.7 transitively depends on slab 0.1.3, 0.3.0, and 0.4.0, via mio 
> 0.5.1,
> mio 0.6.14, and ws-0.7.6. These dependencies are declared as:
> 
> mio-0.5.1/debian/control: librust-slab+default-dev (>= 0.1.0~~),
> mio-0.5.1/debian/control: librust-slab+default-dev (<< 0.2~~),
> ws-0.7.6/debian/control: librust-slab+default-dev (>= 0.3~~),
> ws-0.7.6/debian/control: librust-slab+default-dev (<< 0.4~~),
> mio-0.6.14/debian/control: librust-slab+default-dev (>= 0.4.0~~),
> mio-0.6.14/debian/control: librust-slab+default-dev (<< 0.5~~),

The problem is that you want to depend on independent "major" versions
of these packages, while making using the minor versions available.

I see multiple ways to go about this, but these are based on
assumptions about the versions and the relationships that I'm not sure
hold within Cargo.

The key is, when having an X.Y.Z version in Cargo, not to use that
either as part of the real or virtual package names. Because I
understand you/Cargo care only about the "major" part of that version.

So you could have package slab-X.Y and then depend on just that, or if
for some reason you need to have coinstallability down to the minor
version, then slab-X.Y.Z, in which case that package would provide
slab-X.Y (= X.Y.Z). In addition, all of these would also provide
slab-X (= X.Y.Z) and slab (= X.Y.Z) and probably also slab, so that
you can represent all the range types.

  Cargo deps (A)                dpkg deps (A-X.Y || A-X.Y.Z)
  A                             A
  A (>= 6)                      A (>= 6)
  A (>= 6.1)                    A (>= 6.1)
  A (>= 6.1.3)                  A (>= 6.1.3)
  A (>= 6, << 7)                A-6
  A (>= 6.1, << 6.2)            A-6.1
  A (= 6)                       A-6
  A (= 6.1)                     A-6.1

> RPM has resolved this by implementing "rich dependencies" which is a general
> solution to express "the same package must satisfy both A and B". However, it
> is apparently more difficult to do this in dpkg.

I've not checked how difficult it would be in dpkg. The main problem
here is that the dependency resolution logic is not concentrated just
within dpkg itself, but spread all over the place, and changing/extending
the semantics of these tend to be very painful, and long-winded
processes. So there's general reluctance to do that when there's no
apparent need.

Check for example versioned provides, some people are still afraid to
use them!

> What I am suggesting is smaller: we only need to support two-sided version
> ranges like (>= 0.3~~, << 0.4~~), which does not seem to be such a major
> addition to the existing one-sided version range syntax.

It's still a change in the current semantics, and implies modifying a
ton of projects, I'm afraid.

> In the Debian Rust team, we previously experimented with e.g. converting:
> 
> - Cargo dependencies X (>= 6, << 7) into dpkg dependencies X-6 | X-7
> - Cargo dependencies X (>= 6)       into dpkg dependencies X-6 | X-7 | X-8 | 
> X-9 | X-10

That seems indeed wrong. At least the first should have been just X-6,
and yeah the second should have used more granular versioned provides
as shown above.

> but this causes other problems (unwieldy dependency conversion logic) and does
> not really solve the problem described within this bug report, since it is
> perfectly legal for separate Cargo crates to declare (= 6.1) (= 6.2) (= 6.3)
> dependencies and Cargo will require all three to be installed even though they
> are (supposed to be) semantically compatible. In this case, we would have to
> create new Debian packages called X-6.i (Provides: X-6 = 6.i) for i={1,2,3}
> which puts us right back where we started. (Although granted it is unlikely,
> assuming that everyone in the Rust ecosystem is sane and avoids this forever.)
> 
> So the cleanest solution to this problem would be to support two-sided version
> ranges. This would be beneficial across the whole archive as well - I have
> needed to do the Depends: X (>= 4), X (<< 5) hack before when what I really
> meant was (>= 4, << 5), and I remember this in others' packages too.

Depends on how you define cleanest. :) In this case that means you
cannot use this anyway until a stable+1 (assuming just dpkg and apt
implemented this right away), and it would not be usable anyway until
most major dependency parsers/satisfiers would support it too, which
might be stable+2, perhaps.

Of course the counter-proposal that David and me provided assumes the
versions have sane semantics (similar to semver), and that their
format is uniform all over Cargo. If that's not the case then that
might be a problem, if you are converting the dependencies
automatically. :)

Thanks,
Guillem

Reply via email to