[pacman-dev] [PATCH] Add interactive provider selection
If there are multiple providers in one db, pacman used to just stop at the first one (both during dependency resolution or for pacman -S 'provision' which uses the same code). This adds a new conversation callback so that the user can choose which provider to install. By default (user press enter or --noconfirm), the first provider is still chosen, so for example the behavior of sync402 and 403 is preserved. But at least the user now has the possibility to make the right choice in a manual run. $ pacman -S community/smtp-server :: There are 3 providers available for smtp-server: 1) courier-mta 2) esmtp 3) exim Which one do you want to install? Enter a number (default=1): Signed-off-by: Xavier Chantry <chantry.xavier@gmail.com> --- lib/libalpm/alpm.h | 1 + lib/libalpm/deps.c | 28 +++++++++++++++++++++- src/pacman/callback.c | 13 ++++++++++ src/pacman/util.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ src/pacman/util.h | 2 + 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 237e235..d9e5439 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -379,6 +379,7 @@ typedef enum _pmtransconv_t { PM_TRANS_CONV_CORRUPTED_PKG = (1 << 3), PM_TRANS_CONV_LOCAL_NEWER = (1 << 4), PM_TRANS_CONV_REMOVE_PKGS = (1 << 5), + PM_TRANS_CONV_SELECT_PROVIDER = (1 << 6), } pmtransconv_t; /* Transaction Progress */ diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c index cdaf524..f94f16c 100644 --- a/lib/libalpm/deps.c +++ b/lib/libalpm/deps.c @@ -510,6 +510,10 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, { alpm_list_t *i, *j; int ignored = 0; + + alpm_list_t *providers = NULL; + int count; + /* 1. literals */ for(i = dbs; i; i = i->next) { pmpkg_t *pkg = _alpm_db_get_pkgfromcache(i->data, dep->name); @@ -549,11 +553,31 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, continue; } } - _alpm_log(PM_LOG_WARNING, _("provider package was selected (%s provides %s)\n"), - pkg->name, dep->name); + _alpm_log(PM_LOG_DEBUG, "provider found (%s provides %s)\n", + pkg->name, dep->name); + providers = alpm_list_add(providers, pkg); + /* keep looking for other providers in the current db */ + } + } + /* if there is more than one provider, we ask the user */ + count = alpm_list_count(providers); + if(count == 1) { + pmpkg_t *pkg = alpm_list_getdata(alpm_list_first(providers)); + alpm_list_free(providers); + return(pkg); + } else if(count > 1) { + /* default to first provider if there is no QUESTION callback */ + int index = 0; + QUESTION(handle->trans, PM_TRANS_CONV_SELECT_PROVIDER, + providers, dep, NULL, &index); + if(index >= 0 && index < count) { + pmpkg_t *pkg = alpm_list_getdata(alpm_list_nth(providers, index)); + alpm_list_free(providers); return(pkg); } } + alpm_list_free(providers); + providers = NULL; } if(ignored) { /* resolvedeps will override these */ pm_errno = PM_ERR_PKG_IGNORED; diff --git a/src/pacman/callback.c b/src/pacman/callback.c index 32dafb5..f54fe4a 100644 --- a/src/pacman/callback.c +++ b/src/pacman/callback.c @@ -296,6 +296,19 @@ void cb_trans_conv(pmtransconv_t event, void *data1, void *data2, alpm_list_free(namelist); } break; + case PM_TRANS_CONV_SELECT_PROVIDER: + { + alpm_list_t *providers = (alpm_list_t *)data1; + int count = alpm_list_count(providers); + char *depstring = alpm_dep_compute_string((pmdepend_t *)data2); + printf(_(":: There are %d providers available for %s:\n"), count, + depstring); + free(depstring); + select_display(providers); + printf("\n"); + *response = select_question(count, _("Which one do you want to install?")); + } + break; case PM_TRANS_CONV_LOCAL_NEWER: if(!config->op_s_downloadonly) { *response = yesno(_(":: %s-%s: local version is newer. Upgrade anyway?"), diff --git a/src/pacman/util.c b/src/pacman/util.c index 5b4b2e8..0af36b4 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -686,6 +686,67 @@ void display_optdepends(pmpkg_t *pkg) } } +void select_display(const alpm_list_t *pkglist) +{ + const alpm_list_t *i; + int nth = 1; + alpm_list_t *list = NULL; + char *string = NULL; + + for (i = pkglist; i; i = i->next) { + string = NULL; + pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(i->data)); + list = alpm_list_add(list, string); + nth++; + } + list_display(" ", list); + FREELIST(list); +} + +int select_question(int count, char *fmt, ...) +{ + char response[32]; + FILE *stream; + va_list args; + int preset = 1; + + if(config->noconfirm) { + stream = stdout; + } else { + /* Use stderr so questions are always displayed when redirecting output */ + stream = stderr; + } + + va_start(args, fmt); + vfprintf(stream, fmt, args); + va_end(args); + + fprintf(stream, "\n"); + fprintf(stream, _("Enter a number (default=%d)"), preset); + fprintf(stream, ": "); + + if(config->noconfirm) { + fprintf(stream, "\n"); + return(preset-1); + } + + if(fgets(response, sizeof(response), stdin)) { + strtrim(response); + if(strlen(response) > 0) { + char *endptr = NULL; + int n = strtol(response, &endptr, 10); + if(*endptr == '\0' && n >= 1 && n <= count) { + return(n-1); + } else { + fprintf(stream, _("Invalid number: %s\n"), response); + return(-1); + } + } + } + return(preset-1); +} + + /* presents a prompt and gets a Y/N answer */ static int question(short preset, char *fmt, va_list args) { diff --git a/src/pacman/util.h b/src/pacman/util.h index 2e77b0c..7e25b55 100644 --- a/src/pacman/util.h +++ b/src/pacman/util.h @@ -58,6 +58,8 @@ void display_targets(const alpm_list_t *pkgs, int install); void display_new_optdepends(pmpkg_t *oldpkg, pmpkg_t *newpkg); void display_optdepends(pmpkg_t *pkg); void print_packages(const alpm_list_t *packages); +void select_display(const alpm_list_t *pkglist); +int select_question(int count, char *fmt, ...); int yesno(char *fmt, ...); int noyes(char *fmt, ...); int pm_printf(pmloglevel_t level, const char *format, ...) __attribute__((format(printf,2,3))); -- 1.7.3.1
On 2010-10-19 21:10 +0200 (42:2) Xavier Chantry wrote:
If there are multiple providers in one db, pacman used to just stop at the first one (both during dependency resolution or for pacman -S 'provision' which uses the same code).
This adds a new conversation callback so that the user can choose which provider to install. By default (user press enter or --noconfirm), the first provider is still chosen, so for example the behavior of sync402 and 403 is preserved. But at least the user now has the possibility to make the right choice in a manual run.
Is the install size shown after each package when ShowSize is enabled? That would be useful when deciding which package to install. It would be even more useful to know the cumulative size, i.e. the sum of install sizes of that package and its own dependencies. I think that would be the deciding factor in most cases. Regards, Xyne
Xyne wrote:
Is the install size shown after each package when ShowSize is enabled? That would be useful when deciding which package to install. It would be even more useful to know the cumulative size, i.e. the sum of install sizes of that package and its own dependencies. I think that would be the deciding factor in most cases.
No, size is not shown. It could be though. Cumulative size is an interesting problem. You would need to know how dependencies will be resolved while you are resolving them. If you want, just go ahead. But if it adds too much code, it will probably never be merged.
On 20/10/10 05:10, Xavier Chantry wrote:
If there are multiple providers in one db, pacman used to just stop at the first one (both during dependency resolution or for pacman -S 'provision' which uses the same code).
This adds a new conversation callback so that the user can choose which provider to install. By default (user press enter or --noconfirm), the first provider is still chosen, so for example the behavior of sync402 and 403 is preserved. But at least the user now has the possibility to make the right choice in a manual run.
$ pacman -S community/smtp-server :: There are 3 providers available for smtp-server: 1) courier-mta 2) esmtp 3) exim
Which one do you want to install? Enter a number (default=1):
Signed-off-by: Xavier Chantry<chantry.xavier@gmail.com>
I like the idea, but does this not work cross repos? allan@mugen /home/arch/code/pacman (xavier)
sudo src/pacman/pacman -S java-runtime resolving dependencies... looking for inter-conflicts...
Targets (2): ca-certificates-java-20100412-1 openjdk6-6.b20_1.9.1-1 Total Download Size: 0.00 MB Total Installed Size: 121.53 MB Proceed with installation? [Y/n] I get no choice between openjdk6 and jre with that. Allan
On Thu, Nov 4, 2010 at 11:00 AM, Allan McRae <allan@archlinux.org> wrote:
On 20/10/10 05:10, Xavier Chantry wrote:
If there are multiple providers in one db, pacman used to just stop at the first one (both during dependency resolution or for pacman -S 'provision' which uses the same code).
I like the idea, but does this not work cross repos?
That's indeed what I tried to describe above. I hesitated on this point, but finally I thought this behavior was more consistent with normal target (literal) lookup or group members lookup in the db. It's always the first repo that has the priority and others are ignored. It should not be hard to change the behavior of _alpm_resolvedep though.
On 05/11/10 07:51, Xavier Chantry wrote:
On Thu, Nov 4, 2010 at 11:00 AM, Allan McRae<allan@archlinux.org> wrote:
On 20/10/10 05:10, Xavier Chantry wrote:
If there are multiple providers in one db, pacman used to just stop at the first one (both during dependency resolution or for pacman -S 'provision' which uses the same code).
I like the idea, but does this not work cross repos?
That's indeed what I tried to describe above. I hesitated on this point, but finally I thought this behavior was more consistent with normal target (literal) lookup or group members lookup in the db. It's always the first repo that has the priority and others are ignored.
It should not be hard to change the behavior of _alpm_resolvedep though.
I think that if we are providing a selection dialog for provides then it should present packages from all repos to choose from. Otherwise it seems a half feature to me. Allan
On Fri, Nov 5, 2010 at 4:55 AM, Allan McRae <allan@archlinux.org> wrote:
I think that if we are providing a selection dialog for provides then it should present packages from all repos to choose from. Otherwise it seems a half feature to me.
So my working branch provides a full feature now ! Mostly first patch : Add interactive provider selection and next to last : select_display: per-database output
On 23/01/11 10:58, Xavier Chantry wrote:
On Fri, Nov 5, 2010 at 4:55 AM, Allan McRae<allan@archlinux.org> wrote:
I think that if we are providing a selection dialog for provides then it should present packages from all repos to choose from. Otherwise it seems a half feature to me.
So my working branch provides a full feature now ! Mostly first patch : Add interactive provider selection and next to last : select_display: per-database output
sudo src/pacman/pacman -S smtp-server :: There are 4 providers available for smtp-server: :: Repository testing 1) postfix :: Repository extra 2) postfix :: Repository community 3) courier-mta 4) exim
Which one do you want to install? Enter a number (default=1): Awesome! Incredibly minor and pedantic points... The blank line between the final line of providers and "Which one do you want to install?" is not there when selecting members of a group: e.g. 53) udev 54) usbutils 55) util-linux-ng 56) vi 57) wget 58) which 59) wpa_supplicant 60) xfsprogs Which ones do you want to install? Enter a selection (default=all): That should be made consistent. Also, perhaps change the "one" to "package" in the messages: Which ones do you want to install? to: Which packages do you want to install? Allan
On Sun, Jan 23, 2011 at 2:39 AM, Allan McRae <allan@archlinux.org> wrote:
Awesome! Incredibly minor and pedantic points... The blank line between the final line of providers and "Which one do you want to install?" is not there when selecting members of a group:
e.g.
53) udev 54) usbutils 55) util-linux-ng 56) vi 57) wget 58) which 59) wpa_supplicant 60) xfsprogs Which ones do you want to install? Enter a selection (default=all):
That should be made consistent.
done
Also, perhaps change the "one" to "package" in the messages:
done
Which ones do you want to install? to: Which packages do you want to install?
done
participants (4)
-
Allan McRae
-
Xavier
-
Xavier Chantry
-
Xyne