124 lines
4.7 KiB
Diff
124 lines
4.7 KiB
Diff
|
From 17b724b5d345f5e9d622bcadf27c57baa102bbba Mon Sep 17 00:00:00 2001
|
||
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||
|
Date: Tue, 13 Oct 2015 13:42:57 +0100
|
||
|
Subject: [PATCH 7/7] rpm: Choose providers better (RHBZ#1266918).
|
||
|
|
||
|
In the referenced bug, a customer had installed a web browser called
|
||
|
'palemoon'. The RPM of this web browser provides and requires various
|
||
|
core libraries, such as:
|
||
|
|
||
|
Provides: libnss3.so()(64bit) # normally provided by 'nss'
|
||
|
Requires: libxul.so()(64bit) # normally provided by 'firefox'
|
||
|
|
||
|
Our previous algorithm -- inherited from the days when we used to run
|
||
|
'rpm' commands -- takes every provider of a particular requirement and
|
||
|
adds it to a big list, so if any other package requires
|
||
|
'libnss3.so()(64bit)', then both 'nss' and 'palemoon' are added to the
|
||
|
package list.
|
||
|
|
||
|
Yum used to handle this differently - it used to only pick the package
|
||
|
with the shortest name. Later on, before yum was retired, it had a
|
||
|
more complex decision algorithm described here:
|
||
|
http://yum.baseurl.org/wiki/CompareProviders
|
||
|
|
||
|
This change makes supermin use the shortest name algorithm, so in the
|
||
|
case above, it always picks 'nss' over 'palemoon'.
|
||
|
|
||
|
There is a second possible problem which is not fixed by the current
|
||
|
patch set: If a package both provides and requires the same
|
||
|
dependency, we should ignore that dependency. For example, 'palemoon'
|
||
|
both Provides and Requires 'libxul.so()(64bit)', so if 'palemoon' is
|
||
|
pulled in, then 'firefox' would not be an additional requirement.
|
||
|
Because we store all the packages in a big list, we lose track of
|
||
|
where a dependency originates from, so it is not easy to implement
|
||
|
this second change.
|
||
|
---
|
||
|
src/rpm.ml | 64 ++++++++++++++++++++++++++++++++++++++++++++++----------------
|
||
|
1 file changed, 48 insertions(+), 16 deletions(-)
|
||
|
|
||
|
diff --git a/src/rpm.ml b/src/rpm.ml
|
||
|
index 4d31472..1db47b6 100644
|
||
|
--- a/src/rpm.ml
|
||
|
+++ b/src/rpm.ml
|
||
|
@@ -210,8 +210,51 @@ let rpm_package_name pkg =
|
||
|
let rpm_get_package_database_mtime () =
|
||
|
(lstat "/var/lib/rpm/Packages").st_mtime
|
||
|
|
||
|
-(* Memo of resolved provides. *)
|
||
|
-let rpm_providers = Hashtbl.create 13
|
||
|
+(* Return the best provider of a particular RPM requirement.
|
||
|
+ *
|
||
|
+ * There may be multiple, or no providers. In case there are multiple,
|
||
|
+ * choose the one with the shortest name (as yum used to).
|
||
|
+ *
|
||
|
+ * We could do better here: http://yum.baseurl.org/wiki/CompareProviders
|
||
|
+ *)
|
||
|
+let provider =
|
||
|
+ (* Memo of resolved provides. *)
|
||
|
+ let rpm_providers = Hashtbl.create 13 in
|
||
|
+ fun req ->
|
||
|
+ try Hashtbl.find rpm_providers req
|
||
|
+ with Not_found ->
|
||
|
+ let ret =
|
||
|
+ try
|
||
|
+ (* 'providers' here is an array of just package names. *)
|
||
|
+ let providers = rpm_pkg_whatprovides (get_rpm ()) req in
|
||
|
+ let providers = Array.to_list providers in
|
||
|
+ (* --whatprovides will return duplicate identical packages, so: *)
|
||
|
+ let providers = sort_uniq providers in
|
||
|
+ (* Only packages which are actually installed: *)
|
||
|
+ let providers =
|
||
|
+ List.filter (fun name -> rpm_package_of_string name <> None)
|
||
|
+ providers in
|
||
|
+
|
||
|
+ match providers with
|
||
|
+ | [] -> None
|
||
|
+ | [name] ->
|
||
|
+ Some name
|
||
|
+ | names ->
|
||
|
+ if !settings.debug >= 2 then
|
||
|
+ printf "supermin: rpm: multiple providers: requirement %s: providers: %s\n"
|
||
|
+ req (String.concat " " names);
|
||
|
+ let cmp name1 name2 =
|
||
|
+ let len1 = String.length name1 and len2 = String.length name2 in
|
||
|
+ if len1 <> len2 then compare len1 len2
|
||
|
+ else compare name1 name2 in
|
||
|
+ let names = List.sort cmp names in
|
||
|
+ let best_name = List.hd names in
|
||
|
+ if !settings.debug >= 2 then
|
||
|
+ printf "supermin: rpm: multiple providers: picked %s\n" best_name;
|
||
|
+ Some best_name
|
||
|
+ with Not_found -> None in
|
||
|
+ Hashtbl.add rpm_providers req ret;
|
||
|
+ ret
|
||
|
|
||
|
let rpm_get_all_requires pkgs =
|
||
|
let get pkg =
|
||
|
@@ -226,20 +269,9 @@ let rpm_get_all_requires pkgs =
|
||
|
rpm_pkg_requires (get_rpm ()) (rpm_package_to_string pkg) in
|
||
|
let pkgs' = Array.fold_left (
|
||
|
fun set x ->
|
||
|
- try
|
||
|
- let provides =
|
||
|
- try Hashtbl.find rpm_providers x
|
||
|
- with Not_found ->
|
||
|
- let p = rpm_pkg_whatprovides (get_rpm ()) x in
|
||
|
- Hashtbl.add rpm_providers x p;
|
||
|
- p in
|
||
|
- Array.fold_left (
|
||
|
- fun newset p ->
|
||
|
- match rpm_package_of_string p with
|
||
|
- | None -> newset
|
||
|
- | Some x -> StringSet.add p newset
|
||
|
- ) set provides
|
||
|
- with Not_found -> set
|
||
|
+ match provider x with
|
||
|
+ | None -> set
|
||
|
+ | Some p -> StringSet.add p set
|
||
|
) StringSet.empty reqs in
|
||
|
pkgs'
|
||
|
in
|
||
|
--
|
||
|
2.5.0
|
||
|
|