It comes up with increasing regularity that we don't have a way to represent ranges of versions for a single dependency (c.f. RHBZ[1] and RPM GitHub[2]). Most language-specific dependency managers (e.g. pip for Python, gem for Ruby, composer for PHP, cargo for Rust, npm for Nodejs, whatever Golang people use, etc.) have some variation of ranged dependency processing. These ranged dependencies express a single solvable unit in the form of something like "1.0 < foo <= 1.5" or something similar.
In RPM, we can currently somewhat represent this relationship in two ways: 1. Using a boolean dependency. Example: ``` Requires: (foo > 1.0 and foo <= 1.5) ``` 2. Using (Build)Requires + (Build)Conflicts Example: ``` Requires: foo > 1.0 Conflicts: foo > 1.5 ``` However, there are weaknesses to each approach that make this non-functional. Consider the boolean dependency form. Suppose an install transaction processes two boolean deps, one representing "1.0 < foo <= 1.5" and one representing "2.0 < foo <= 2.5". In the case where nothing is installed, this works as expected and installs "foo" and "bar". However, in a situation where "foo" is already installed, "bar" never gets installed because the condition is satisifed. This leads to a broken result (as exemplified in this GitHub bug[3]). This is because each of the components of a boolean dependency is treated as a separate solvable condition (that is, instead of treating as something to define a single dependency, it could evaluate to multiple). That means that it's also possible to trigger the installation of multiple packages when it isn't desired. Now consider the Requires + Conflicts form. Suppose a transaction processes the same two ranges in the form of Requires + Conflicts. The result leads to an invalid transaction, as the second range is completely invalidated by the first. This is especially problematic for long chains of builddeps in ecosystems like Rust and Golang, where this could fundamentally stop something from getting built. Ecosystems like Nodejs would be completely broken for runtime installations by this type of dependency description. As it turns out, libsolv does support a dependency operand to fix this: `REL_WITH` (expressed as `+` in libsolv testcases). This binds two clauses together as a single dependency expression for the resolver. So, a potential solution for properly express this in rpm would be to add a `with` statement that provides this logic for libsolv and dependency resolvers on top of it (DNF and Zypper) to use. So, for example, "1.0 < foo <= 1.5" and "2.0 < foo <= 2.5" would be expressed as: ``` Requires: ((foo > 1.0 with foo <= 1.5) and (foo > 2.0 with foo <= 2.5)) ``` The expression above indicates that two packages will be installed, each satisfying one of the clauses. "foo" will get installed from the first clause, and "bar" will get installed from the second clause. This implements the range concept using the "with" operator, ensuring that the expression is interpreted to mean one and exactly one dependency. This grants us the ability to properly handle semantically versioned dependencies that are common in many libraries now without the side-effects mentioned earlier. [1]: https://bugzilla.redhat.com/show_bug.cgi?id=1389871 [2]: https://github.com/rpm-software-management/rpm/issues/159 [3]: https://github.com/openSUSE/libsolv/issues/182 -- 真実はいつも一つ!/ Always, there's only one truth! _______________________________________________ Rpm-maint mailing list Rpm-maint@lists.rpm.org http://lists.rpm.org/mailman/listinfo/rpm-maint