[pacman-dev] [PATCH v3 3/3] Add --assume-installed option

Andrew Gregory andrew.gregory.8 at gmail.com
Wed Sep 17 10:30:25 EDT 2014


On 09/17/14 at 01:02am, Florian Pritz wrote:
> This allows to ignore specific dependencies.
> 
> Signed-off-by: Florian Pritz <bluewind at xinu.at>
> ---
>  doc/pacman.8.txt                       |  6 +++
>  lib/libalpm/alpm.h                     | 11 +++++
>  lib/libalpm/deps.c                     |  8 +++-
>  lib/libalpm/handle.c                   | 79 ++++++++++++++++++++++++++++++++++
>  lib/libalpm/handle.h                   |  1 +
>  src/pacman/conf.c                      | 17 ++++++++
>  src/pacman/conf.h                      |  4 +-
>  src/pacman/pacman.c                    |  4 ++
>  test/pacman/tests/TESTS                |  3 ++
>  test/pacman/tests/assumeinstalled.py   | 20 +++++++++
>  test/pacman/tests/assumeinstalled_2.py | 11 +++++
>  test/pacman/tests/assumeinstalled_3.py | 14 ++++++
>  12 files changed, 175 insertions(+), 3 deletions(-)
>  create mode 100644 test/pacman/tests/assumeinstalled.py
>  create mode 100644 test/pacman/tests/assumeinstalled_2.py
>  create mode 100644 test/pacman/tests/assumeinstalled_3.py
> 
> diff --git a/doc/pacman.8.txt b/doc/pacman.8.txt
> index 152b261..45741b4 100644
> --- a/doc/pacman.8.txt
> +++ b/doc/pacman.8.txt
> @@ -186,6 +186,12 @@ Transaction Options (apply to '-S', '-R' and '-U')
>  	dependencies are installed and there are no package conflicts in the
>  	system. Specify this option twice to skip all dependency checks.
>  
> +*\--assume-installed* <package=version>::
> +	Add a virtual package "package" with version "version" to the transaction
> +	to satisfy dependencies. This allows to disable specific dependency checks
> +	without affecting all dependency checks. To disable all dependency
> +	checking, see the '\--nodeps' option.
> +
>  *\--dbonly*::
>  	Adds/removes the database entry only, leaving all files in place.
>  
> diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
> index 12769a4..9a35454 100644
> --- a/lib/libalpm/alpm.h
> +++ b/lib/libalpm/alpm.h
> @@ -857,6 +857,17 @@ int alpm_option_set_ignoregroups(alpm_handle_t *handle, alpm_list_t *ignoregrps)
>  int alpm_option_remove_ignoregroup(alpm_handle_t *handle, const char *grp);
>  /** @} */
>  
> +/** @name Accessors to the list of ignored dependencies.
> + * These functions modify the list of dependencies that
> + * should be ignored by a sysupgrade.
> + * @{
> + */
> +alpm_list_t *alpm_option_get_assumeinstalled(alpm_handle_t *handle);
> +int alpm_option_add_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep);
> +int alpm_option_set_assumeinstalled(alpm_handle_t *handle, alpm_list_t *deps);
> +int alpm_option_remove_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep);
> +/** @} */
> +
>  /** Returns the targeted architecture. */
>  const char *alpm_option_get_arch(alpm_handle_t *handle);
>  /** Sets the targeted architecture. */
> diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c
> index 0ef5756..55c47a1 100644
> --- a/lib/libalpm/deps.c
> +++ b/lib/libalpm/deps.c
> @@ -333,8 +333,10 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_handle_t *handle,
>  			}
>  			/* 1. we check the upgrade list */
>  			/* 2. we check database for untouched satisfying packages */
> +			/* 3. we check the dependency ignore list */
>  			if(!find_dep_satisfier(upgrade, depend) &&
> -					!find_dep_satisfier(dblist, depend)) {
> +					!find_dep_satisfier(dblist, depend) &&
> +					!_alpm_depcmp_provides(depend, handle->assumeinstalled)) {
>  				/* Unsatisfied dependency in the upgrade list */
>  				alpm_depmissing_t *miss;
>  				char *missdepstring = alpm_dep_compute_string(depend);
> @@ -363,9 +365,11 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_handle_t *handle,
>  				/* we won't break this depend, if it is already broken, we ignore it */
>  				/* 1. check upgrade list for satisfiers */
>  				/* 2. check dblist for satisfiers */
> +				/* 3. we check the dependency ignore list */
>  				if(causingpkg &&
>  						!find_dep_satisfier(upgrade, depend) &&
> -						!find_dep_satisfier(dblist, depend)) {
> +						!find_dep_satisfier(dblist, depend) &&
> +						!_alpm_depcmp_provides(depend, handle->assumeinstalled)) {
>  					alpm_depmissing_t *miss;
>  					char *missdepstring = alpm_dep_compute_string(depend);
>  					_alpm_log(handle, ALPM_LOG_DEBUG, "checkdeps: transaction would break '%s' dependency of '%s'\n",
> diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c
> index fdd269b..0f88462 100644
> --- a/lib/libalpm/handle.c
> +++ b/lib/libalpm/handle.c
> @@ -37,6 +37,7 @@
>  #include "delta.h"
>  #include "trans.h"
>  #include "alpm.h"
> +#include "deps.h"
>  
>  alpm_handle_t *_alpm_handle_new(void)
>  {
> @@ -86,6 +87,10 @@ void _alpm_handle_free(alpm_handle_t *handle)
>  	FREELIST(handle->noextract);
>  	FREELIST(handle->ignorepkg);
>  	FREELIST(handle->ignoregroup);
> +
> +	alpm_list_free_inner(handle->assumeinstalled, (alpm_list_fn_free)alpm_dep_free);
> +	alpm_list_free(handle->assumeinstalled);
> +
>  	FREE(handle);
>  }
>  
> @@ -246,6 +251,30 @@ alpm_list_t SYMEXPORT *alpm_option_get_ignoregroups(alpm_handle_t *handle)
>  	return handle->ignoregroup;
>  }
>  
> +alpm_list_t SYMEXPORT *alpm_option_get_assumeinstalled(alpm_handle_t *handle)
> +{
> +	alpm_list_t *ret = NULL;
> +	CHECK_HANDLE(handle, return NULL);
> +
> +	for(alpm_list_t *i = handle->assumeinstalled; i; i = i->next) {
> +		alpm_depend_t *dep = i->data;
> +		char *pkg = NULL;
> +		int len = strlen(dep->name) + strlen("=") + strlen(dep->version) + 1;
> +		MALLOC(pkg, len, goto error);
> +		if(snprintf(pkg, len, "%s=%s", dep->name, dep->version) != len - 1) {
> +			_alpm_log(handle, ALPM_LOG_DEBUG, "Failed to assemble assumeinstalled string\n");
> +			goto error;
> +		}
> +		ret = alpm_list_add(ret, pkg);
> +	 }
> +	return ret;
> +
> +error:
> +	alpm_list_free_inner(ret, (alpm_list_fn_free)alpm_dep_free);
> +	alpm_list_free(ret);
> +	return NULL;

You're still returning a list of strings...

> +}
> +
>  const char SYMEXPORT *alpm_option_get_arch(alpm_handle_t *handle)
>  {
>  	CHECK_HANDLE(handle, return NULL);
> @@ -545,6 +574,56 @@ int SYMEXPORT alpm_option_remove_ignoregroup(alpm_handle_t *handle, const char *
>  	return _alpm_option_strlist_rem(handle, &(handle->ignoregroup), grp);
>  }
>  
> +int SYMEXPORT alpm_option_add_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep)
> +{
> +	CHECK_HANDLE(handle, return -1);
> +
> +	handle->assumeinstalled = alpm_list_add(handle->assumeinstalled, (void *)dep);
> +	return 0;
> +}
> +
> +int SYMEXPORT alpm_option_set_assumeinstalled(alpm_handle_t *handle, alpm_list_t *deps)
> +{
> +	CHECK_HANDLE(handle, return -1);
> +	if(handle->assumeinstalled) {
> +		alpm_list_free_inner(handle->assumeinstalled, (alpm_list_fn_free)alpm_dep_free);
> +		alpm_list_free(handle->assumeinstalled);
> +	}
> +	for(alpm_list_t *i = deps; i; i = i->next) {
> +		int ret = alpm_option_add_assumeinstalled(handle, i->data);
> +		if(ret) {
> +			return ret;
> +		}
> +	 }

No need to bother with add_assumeinstalled anymore.  This can be
a simple assignment like (most of) our other setters.

> +	return 0;
> +}
> +
> +static int assumeinstalled_cmp(const void *data, const void *pkg)

data and pkg are misleading names, convention would have d1 and d2.

> +{
> +	const alpm_depend_t *dep1 = ((alpm_list_t*)data)->data;

The comparison function gets the list item data, not an alpm_list_t*.

> +	const alpm_depend_t *dep2 = pkg;
> +
> +	if(strcmp(dep1->name, dep2->name) && strcmp(dep1->version, dep2->version)) {

Those should both be strcmp(...) == 0.

> +		return 0;
> +	}
> +
> +	return -1;
> +}
> +
> +int SYMEXPORT alpm_option_remove_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep)
> +{
> +	alpm_depend_t *vdata = NULL;
> +	CHECK_HANDLE(handle, return -1);
> +
> +	handle->assumeinstalled = alpm_list_remove(handle->assumeinstalled, dep, &assumeinstalled_cmp, (void **)&vdata);
> +	if(vdata != NULL) {
> +		alpm_dep_free(vdata);
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
>  int SYMEXPORT alpm_option_set_arch(alpm_handle_t *handle, const char *arch)
>  {
>  	CHECK_HANDLE(handle, return -1);
> diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h
> index 85c64f6..c9c454a 100644
> --- a/lib/libalpm/handle.h
> +++ b/lib/libalpm/handle.h
> @@ -83,6 +83,7 @@ struct __alpm_handle_t {
>  	alpm_list_t *noextract;   /* List of files NOT to extract */
>  	alpm_list_t *ignorepkg;   /* List of packages to ignore */
>  	alpm_list_t *ignoregroup; /* List of groups to ignore */
> +	alpm_list_t *assumeinstalled;   /* List of virtual packages used to satisfy dependencies */
>  
>  	/* options */
>  	char *arch;              /* Architecture of packages we should allow */
> diff --git a/src/pacman/conf.c b/src/pacman/conf.c
> index 0e483f7..4573e8e 100644
> --- a/src/pacman/conf.c
> +++ b/src/pacman/conf.c
> @@ -134,6 +134,7 @@ int config_free(config_t *oldconfig)
>  	FREELIST(oldconfig->holdpkg);
>  	FREELIST(oldconfig->ignorepkg);
>  	FREELIST(oldconfig->ignoregrp);
> +	FREELIST(oldconfig->assumeinstalled);
>  	FREELIST(oldconfig->noupgrade);
>  	FREELIST(oldconfig->noextract);
>  	free(oldconfig->configfile);
> @@ -741,6 +742,22 @@ static int setup_libalpm(void)
>  	alpm_option_set_noupgrades(handle, config->noupgrade);
>  	alpm_option_set_noextracts(handle, config->noextract);
>  
> +	for(alpm_list_t *i = config->assumeinstalled; i; i = i->next) {

i should be declared at the top of the block.

> +		char *entry = i->data;
> +		alpm_depend_t *dep = alpm_splitdep(entry);
> +		if(!dep) {
> +			pm_printf(ALPM_LOG_WARNING, _("Invalid format for assume installed entry. Skipping '%s'\n"), entry);

This is inconsistent.  It's printed as a warning and says it's just
skipping the invalid depend but then aborts pacman.  Also, please wrap
this closer to 80 columns.

> +			alpm_dep_free(dep);

dep is NULL, there's nothing to free here.

> +			return 1;
> +		}
> +		pm_printf(ALPM_LOG_DEBUG, "parsed assume installed: %s %s\n", dep->name, dep->version);
> +
> +		ret = alpm_option_add_assumeinstalled(handle, dep);
> +		if(ret) {

dep should be freed here and there should be an error message before
aborting.

> +			return ret;
> +		}
> +	 }
> +
>  	return 0;
>  }
>  
> diff --git a/src/pacman/conf.h b/src/pacman/conf.h
> index e8cac50..8aed6d6 100644
> --- a/src/pacman/conf.h
> +++ b/src/pacman/conf.h
> @@ -101,6 +101,7 @@ typedef struct __config_t {
>  	alpm_list_t *holdpkg;
>  	alpm_list_t *ignorepkg;
>  	alpm_list_t *ignoregrp;
> +	alpm_list_t *assumeinstalled;
>  	alpm_list_t *noupgrade;
>  	alpm_list_t *noextract;
>  	char *xfercommand;
> @@ -176,7 +177,8 @@ enum {
>  	OP_UNNEEDED,
>  	OP_VERBOSE,
>  	OP_DOWNLOADONLY,
> -	OP_REFRESH
> +	OP_REFRESH,
> +	OP_ASSUMEINSTALLED
>  };
>  
>  /* clean method */
> diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
> index 345fb0a..e079726 100644
> --- a/src/pacman/pacman.c
> +++ b/src/pacman/pacman.c
> @@ -632,6 +632,9 @@ static int parsearg_trans(int opt)
>  			free(config->print_format);
>  			config->print_format = strdup(optarg);
>  			break;
> +		case OP_ASSUMEINSTALLED:
> +			parsearg_util_addlist(&(config->assumeinstalled));
> +			break;
>  		default:
>  			return 1;
>  	}
> @@ -855,6 +858,7 @@ static int parseargs(int argc, char *argv[])
>  		{"noconfirm",  no_argument,       0, OP_NOCONFIRM},
>  		{"config",     required_argument, 0, OP_CONFIG},
>  		{"ignore",     required_argument, 0, OP_IGNORE},
> +		{"assume-installed",     required_argument, 0, OP_ASSUMEINSTALLED},
>  		{"debug",      optional_argument, 0, OP_DEBUG},
>  		{"force",      no_argument,       0, OP_FORCE},
>  		{"noprogressbar", no_argument,    0, OP_NOPROGRESSBAR},
> diff --git a/test/pacman/tests/TESTS b/test/pacman/tests/TESTS
> index 1b5a81f..6c3c548 100644
> --- a/test/pacman/tests/TESTS
> +++ b/test/pacman/tests/TESTS
> @@ -1,3 +1,6 @@
> +TESTS += test/pacman/tests/assumeinstalled.py
> +TESTS += test/pacman/tests/assumeinstalled_2.py
> +TESTS += test/pacman/tests/assumeinstalled_3.py

These test names don't follow either our previous naming convention or
the newer descriptive convention.

>  TESTS += test/pacman/tests/clean001.py
>  TESTS += test/pacman/tests/clean002.py
>  TESTS += test/pacman/tests/clean003.py
> diff --git a/test/pacman/tests/assumeinstalled.py b/test/pacman/tests/assumeinstalled.py
> new file mode 100644
> index 0000000..41b7750
> --- /dev/null
> +++ b/test/pacman/tests/assumeinstalled.py
> @@ -0,0 +1,20 @@
> +self.description = "Update a package using --assume-installed"
> +
> +lp1 = pmpkg("pkg1", "1.0-1")
> +
> +lp2 = pmpkg("pkg2", "1.0-1")
> +lp2.depends = ["pkg1=1.0"]
> +
> +sp1 = pmpkg("pkg1", "2.0-1")
> +
> +for p in lp1, lp2:
> +	self.addpkg2db("local", p);
> +
> +self.addpkg2db("sync", sp1);
> +
> +self.args = "-Su --assume-installed pkg1=1.0"
> +
> +self.addrule("PACMAN_RETCODE=0")
> +self.addrule("PKG_VERSION=pkg1|2.0-1")
> +self.addrule("PKG_EXIST=pkg2")
> +self.addrule("PKG_EXIST=pkg1")

PKG_VERSION already requires the package to exist, checking pkg1 again
is redundant.

> diff --git a/test/pacman/tests/assumeinstalled_2.py b/test/pacman/tests/assumeinstalled_2.py
> new file mode 100644
> index 0000000..6744dc8
> --- /dev/null
> +++ b/test/pacman/tests/assumeinstalled_2.py
> @@ -0,0 +1,11 @@
> +self.description = "Install a package using --assume-installed"
> +
> +sp1 = pmpkg("pkg2", "2.0-1")
> +sp1.depends = ["pkg1"]
> +
> +self.addpkg2db("sync", sp1);
> +
> +self.args = "-S pkg2 --assume-installed pkg1"
> +
> +self.addrule("PACMAN_RETCODE=0")
> +self.addrule("PKG_EXIST=pkg2")
> diff --git a/test/pacman/tests/assumeinstalled_3.py b/test/pacman/tests/assumeinstalled_3.py
> new file mode 100644
> index 0000000..4f3630c
> --- /dev/null
> +++ b/test/pacman/tests/assumeinstalled_3.py
> @@ -0,0 +1,14 @@
> +self.description = "Remove a package using --assume-installed"
> +
> +lp1 = pmpkg("pkg1", "1.0-1")
> +
> +lp2 = pmpkg("pkg2", "1.0-1")
> +lp2.depends = ["pkg1=1.0"]
> +for p in lp1, lp2:
> +	self.addpkg2db("local", p);
> +
> +self.args = "-R pkg1 --assume-installed pkg1=1.0"
> +
> +self.addrule("PACMAN_RETCODE=0")
> +self.addrule("PKG_EXIST=pkg2")
> +self.addrule("!PKG_EXIST=pkg1")
> -- 
> 2.1.0


More information about the pacman-dev mailing list