Replaced redundant dependency sorting code with a simpler implementation.

This fixes the issue found by "xbps-src bootstrap" when a package
without "provides" was declared as a virtual package.
This commit is contained in:
Juan RP 2014-10-18 12:35:47 +02:00
parent adc0643ee4
commit ff189fd210
11 changed files with 117 additions and 488 deletions

7
NEWS
View File

@ -1,5 +1,12 @@
xbps-0.42 (???):
* libxbps: replaces the internal redundant dependency sorting step
that was made in xbps_transaction_prepare() with a simple implementation
that fixes 2 bugs and makes xbps 2x faster to resolve dependencies.
Packages that don't have "provides" obj but were declared as virtual packages,
were not detected correctly and lead me to rewrite this code.
* pkgdb: switch to zplists by default.
* Fixed issue #62 (Dup replaced pkgs in transaction); see

View File

@ -184,19 +184,13 @@ xbps_dictionary_t HIDDEN xbps_find_virtualpkg_in_array(struct xbps_handle *, xbp
* @private
* From lib/transaction_revdeps.c
*/
void HIDDEN xbps_transaction_revdeps(struct xbps_handle *);
void HIDDEN xbps_transaction_revdeps(struct xbps_handle *, xbps_array_t);
/**
* @privuate
* From lib/transaction_shlibs.c
*/
bool HIDDEN xbps_transaction_shlibs(struct xbps_handle *);
/**
* @private
* From lib/transaction_sortdeps.c
*/
int HIDDEN xbps_transaction_sort(struct xbps_handle *);
bool HIDDEN xbps_transaction_shlibs(struct xbps_handle *, xbps_array_t, xbps_array_t);
/**
* @private
@ -241,7 +235,7 @@ int HIDDEN xbps_set_cb_state(struct xbps_handle *, xbps_state_t, int,
*/
int HIDDEN xbps_unpack_binary_pkg(struct xbps_handle *, xbps_dictionary_t);
int HIDDEN xbps_transaction_package_replace(struct xbps_handle *);
int HIDDEN xbps_transaction_package_replace(struct xbps_handle *, xbps_array_t);
/**
* @private

View File

@ -38,7 +38,7 @@ OBJS += package_remove.o package_find_obsoletes.o package_state.o
OBJS += package_unpack.o package_register.o package_script.o verifysig.o
OBJS += package_msg.o pkgdb_conversion.o transaction_shlibs.o
OBJS += transaction_commit.o transaction_package_replace.o
OBJS += transaction_dictionary.o transaction_sortdeps.o transaction_ops.o
OBJS += transaction_dictionary.o transaction_ops.o
OBJS += transaction_revdeps.o pubkey2fp.o
OBJS += download.o initend.o pkgdb.o package_conflicts.o
OBJS += plist.o plist_find.o plist_match.o archive.o

View File

@ -40,6 +40,10 @@ store_dependency(struct xbps_handle *xhp,
const char *pkgver;
char *pkgname, *self_replaced;
int rv;
xbps_dictionary_get_cstring_nocopy(repo_pkgd, "pkgver", &pkgver);
if (xbps_find_pkg_in_array(unsorted, pkgver, NULL))
return 0;
/*
* Overwrite package state in dictionary with same state than the
* package currently uses, otherwise not-installed.
@ -53,7 +57,6 @@ store_dependency(struct xbps_handle *xhp,
!xbps_dictionary_set_bool(repo_pkgd, "automatic-install", true))
return EINVAL;
xbps_dictionary_get_cstring_nocopy(repo_pkgd, "pkgver", &pkgver);
/*
* Set a replaces to itself, so that virtual packages are always replaced.
*/
@ -370,23 +373,22 @@ find_repo_deps(struct xbps_handle *xhp,
}
free(pkgname);
free(reqpkgname);
/*
* Package is on repo, add it into the transaction dictionary.
*/
xbps_dictionary_set_cstring_nocopy(curpkgd, "transaction", reason);
rv = store_dependency(xhp, unsorted, curpkgd, state);
if (rv != 0) {
xbps_dbg_printf(xhp, "store_dependency failed for `%s': %s\n", reqpkg, strerror(rv));
break;
}
/*
* If package doesn't have rundeps, pass to the next one.
*/
curpkgrdeps = xbps_dictionary_get(curpkgd, "run_depends");
if (curpkgrdeps == NULL)
if (curpkgrdeps == NULL) {
/*
* Package is on repo, add it into the transaction dictionary.
*/
xbps_dictionary_set_cstring_nocopy(curpkgd, "transaction", reason);
rv = store_dependency(xhp, unsorted, curpkgd, state);
if (rv != 0) {
xbps_dbg_printf(xhp, "store_dependency failed for `%s': %s\n", reqpkg, strerror(rv));
break;
}
continue;
curpkgprovides = xbps_dictionary_get(curpkgd, "provides");
}
if (xhp->flags & XBPS_FLAG_DEBUG) {
xbps_dbg_printf(xhp, "");
@ -399,11 +401,21 @@ find_repo_deps(struct xbps_handle *xhp,
* Recursively find rundeps for current pkg dictionary.
*/
(*depth)++;
curpkgprovides = xbps_dictionary_get(curpkgd, "provides");
rv = find_repo_deps(xhp, unsorted, curpkgrdeps, curpkgprovides, pkgver_q, depth);
if (rv != 0) {
xbps_dbg_printf(xhp, "Error checking %s for rundeps: %s\n", reqpkg, strerror(rv));
break;
}
/*
* Package is on repo, add it into the transaction dictionary.
*/
xbps_dictionary_set_cstring_nocopy(curpkgd, "transaction", reason);
rv = store_dependency(xhp, unsorted, curpkgd, state);
if (rv != 0) {
xbps_dbg_printf(xhp, "store_dependency failed for `%s': %s\n", reqpkg, strerror(rv));
break;
}
}
xbps_object_iterator_release(iter);
(*depth)--;

View File

@ -201,7 +201,7 @@ xbps_transaction_init(struct xbps_handle *xhp)
xhp->transd = NULL;
return ENOMEM;
}
if (!xbps_dictionary_set(xhp->transd, "unsorted_deps", array)) {
if (!xbps_dictionary_set(xhp->transd, "packages", array)) {
xbps_object_release(xhp->transd);
xhp->transd = NULL;
return EINVAL;
@ -250,8 +250,8 @@ xbps_transaction_init(struct xbps_handle *xhp)
int
xbps_transaction_prepare(struct xbps_handle *xhp)
{
xbps_array_t array;
unsigned int i;
xbps_array_t array, pkgs, edges;
unsigned int i, cnt;
int rv = 0;
if (xhp->transd == NULL)
@ -260,22 +260,50 @@ xbps_transaction_prepare(struct xbps_handle *xhp)
/*
* Collect dependencies for pkgs in transaction.
*/
array = xbps_dictionary_get(xhp->transd, "unsorted_deps");
for (i = 0; i < xbps_array_count(array); i++) {
if ((rv = xbps_repository_find_deps(xhp, array, xbps_array_get(array, i))) != 0)
if ((edges = xbps_array_create()) == NULL)
return ENOMEM;
/*
* The edges are also appended after its dependencies have been
* collected; the edges at the original array are removed later.
*/
pkgs = xbps_dictionary_get(xhp->transd, "packages");
assert(xbps_object_type(pkgs) == XBPS_TYPE_ARRAY);
cnt = xbps_array_count(pkgs);
for (i = 0; i < cnt; i++) {
xbps_dictionary_t pkgd;
xbps_string_t str;
pkgd = xbps_array_get(pkgs, i);
str = xbps_dictionary_get(pkgd, "pkgver");
assert(xbps_object_type(str) == XBPS_TYPE_STRING);
if (!xbps_array_add(edges, str))
return ENOMEM;
if ((rv = xbps_repository_find_deps(xhp, pkgs, pkgd)) != 0)
return rv;
if (!xbps_array_add(pkgs, pkgd))
return ENOMEM;
}
/* ... remove dup edges at head */
for (i = 0; i < xbps_array_count(edges); i++) {
const char *pkgver;
xbps_array_get_cstring_nocopy(edges, i, &pkgver);
xbps_remove_pkg_from_array_by_pkgver(pkgs, pkgver);
}
xbps_object_release(edges);
/*
* If there are missing deps or revdeps bail out.
*/
xbps_transaction_revdeps(xhp);
xbps_transaction_revdeps(xhp, pkgs);
array = xbps_dictionary_get(xhp->transd, "missing_deps");
if (xbps_array_count(array))
return ENODEV;
array = xbps_dictionary_get(xhp->transd, "unsorted_deps");
for (i = 0; i < xbps_array_count(array); i++)
xbps_pkg_find_conflicts(xhp, array, xbps_array_get(array, i));
for (i = 0; i < xbps_array_count(pkgs); i++)
xbps_pkg_find_conflicts(xhp, pkgs, xbps_array_get(pkgs, i));
/*
* If there are package conflicts bail out.
*/
@ -285,7 +313,7 @@ xbps_transaction_prepare(struct xbps_handle *xhp)
/*
* Check for packages to be replaced.
*/
if ((rv = xbps_transaction_package_replace(xhp)) != 0) {
if ((rv = xbps_transaction_package_replace(xhp, pkgs)) != 0) {
xbps_object_release(xhp->transd);
xhp->transd = NULL;
return rv;
@ -293,17 +321,9 @@ xbps_transaction_prepare(struct xbps_handle *xhp)
/*
* Check for unresolved shared libraries.
*/
if (xbps_transaction_shlibs(xhp))
if (xbps_transaction_shlibs(xhp, pkgs, xbps_dictionary_get(xhp->transd, "missing_shlibs")))
return ENOEXEC;
/*
* Sort package dependencies if necessary.
*/
if ((rv = xbps_transaction_sort(xhp)) != 0) {
xbps_object_release(xhp->transd);
xhp->transd = NULL;
return rv;
}
/*
* Add transaction stats for total download/installed size,
* number of packages to be installed, updated, configured
@ -317,7 +337,6 @@ xbps_transaction_prepare(struct xbps_handle *xhp)
/*
* Remove now unneeded objects.
*/
xbps_dictionary_remove(xhp->transd, "unsorted");
xbps_dictionary_remove(xhp->transd, "missing_shlibs");
xbps_dictionary_remove(xhp->transd, "missing_deps");
xbps_dictionary_remove(xhp->transd, "conflicts");

View File

@ -62,7 +62,7 @@ static int
trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool reinstall)
{
xbps_dictionary_t pkg_pkgdb = NULL, pkg_repod = NULL;
xbps_array_t unsorted, replaces;
xbps_array_t pkgs, replaces;
const char *repoloc, *repopkgver, *instpkgver, *reason;
char *self_replaced, *pkgname;
int action = 0, rv = 0;
@ -151,13 +151,13 @@ trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool reinstall)
if ((rv = xbps_transaction_init(xhp)) != 0)
return rv;
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
pkgs = xbps_dictionary_get(xhp->transd, "packages");
/*
* Find out if package being updated matches the one already
* in transaction, in that case ignore it.
*/
if (action == TRANS_UPDATE) {
if (xbps_find_pkg_in_array(unsorted, repopkgver, NULL)) {
if (xbps_find_pkg_in_array(pkgs, repopkgver, NULL)) {
xbps_dbg_printf(xhp, "[update] `%s' already queued in "
"transaction.\n", repopkgver);
return EEXIST;
@ -212,9 +212,9 @@ trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool reinstall)
}
/*
* Add the pkg dictionary from repository's index dictionary into
* the "unsorted" queue.
* the packages array.
*/
if (!xbps_array_add(unsorted, pkg_repod)) {
if (!xbps_array_add(pkgs, pkg_repod)) {
free(pkgname);
return EINVAL;
}
@ -290,7 +290,7 @@ xbps_transaction_remove_pkg(struct xbps_handle *xhp,
bool recursive)
{
xbps_dictionary_t pkgd;
xbps_array_t unsorted, orphans, orphans_pkg, reqby;
xbps_array_t pkgs, orphans, orphans_pkg, reqby;
xbps_object_t obj;
const char *pkgver;
unsigned int count;
@ -308,7 +308,7 @@ xbps_transaction_remove_pkg(struct xbps_handle *xhp,
if ((rv = xbps_transaction_init(xhp)) != 0)
return rv;
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
pkgs = xbps_dictionary_get(xhp->transd, "packages");
if (!recursive)
goto rmpkg;
@ -330,7 +330,7 @@ xbps_transaction_remove_pkg(struct xbps_handle *xhp,
obj = xbps_array_get(orphans, count);
xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
xbps_dictionary_set_cstring_nocopy(obj, "transaction", "remove");
xbps_array_add(unsorted, obj);
xbps_array_add(pkgs, obj);
xbps_dbg_printf(xhp, "%s: added into transaction (remove).\n", pkgver);
}
reqby = xbps_pkgdb_get_pkg_revdeps(xhp, pkgname);
@ -347,11 +347,11 @@ xbps_transaction_remove_pkg(struct xbps_handle *xhp,
rmpkg:
/*
* Add pkg dictionary into the transaction unsorted queue.
* Add pkg dictionary into the transaction pkgs queue.
*/
xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
xbps_dictionary_set_cstring_nocopy(pkgd, "transaction", "remove");
xbps_array_add(unsorted, pkgd);
xbps_array_add(pkgs, pkgd);
xbps_dbg_printf(xhp, "%s: added into transaction (remove).\n", pkgver);
reqby = xbps_pkgdb_get_pkg_revdeps(xhp, pkgver);
/*
@ -368,7 +368,7 @@ rmpkg:
int
xbps_transaction_autoremove_pkgs(struct xbps_handle *xhp)
{
xbps_array_t orphans, unsorted;
xbps_array_t orphans, pkgs;
xbps_object_t obj;
const char *pkgver;
unsigned int count;
@ -386,16 +386,16 @@ xbps_transaction_autoremove_pkgs(struct xbps_handle *xhp)
if ((rv = xbps_transaction_init(xhp)) != 0)
goto out;
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
pkgs = xbps_dictionary_get(xhp->transd, "packages");
/*
* Add pkg orphan dictionary into the transaction unsorted queue.
* Add pkg orphan dictionary into the transaction pkgs queue.
*/
while (count--) {
obj = xbps_array_get(orphans, count);
xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
xbps_dictionary_set_cstring_nocopy(obj,
"transaction", "remove");
xbps_array_add(unsorted, obj);
xbps_array_add(pkgs, obj);
xbps_dbg_printf(xhp, "%s: added (remove).\n", pkgver);
}
out:

View File

@ -34,20 +34,16 @@
#include "xbps_api_impl.h"
int HIDDEN
xbps_transaction_package_replace(struct xbps_handle *xhp)
xbps_transaction_package_replace(struct xbps_handle *xhp, xbps_array_t pkgs)
{
xbps_array_t unsorted;
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
for (unsigned int i = 0; i < xbps_array_count(unsorted); i++) {
for (unsigned int i = 0; i < xbps_array_count(pkgs); i++) {
xbps_array_t replaces;
xbps_object_t obj, obj2;
xbps_object_iterator_t iter;
const char *pkgver;
char *pkgname;
obj = xbps_array_get(unsorted, i);
obj = xbps_array_get(pkgs, i);
replaces = xbps_dictionary_get(obj, "replaces");
if (replaces == NULL || xbps_array_count(replaces) == 0)
continue;
@ -89,7 +85,7 @@ xbps_transaction_package_replace(struct xbps_handle *xhp)
/*
* Make sure to not add duplicates.
*/
reppkgd = xbps_find_pkg_in_array(unsorted, curpkgname, NULL);
reppkgd = xbps_find_pkg_in_array(pkgs, curpkgname, NULL);
if (reppkgd) {
xbps_dictionary_get_cstring_nocopy(reppkgd,
"transaction", &tract);
@ -102,12 +98,12 @@ xbps_transaction_package_replace(struct xbps_handle *xhp)
* transaction and it's going to be updated.
*/
xbps_dictionary_get_bool(instd, "automatic-install", &instd_auto);
if ((reppkgd = xbps_find_pkg_in_array(unsorted, curpkgname, NULL))) {
if ((reppkgd = xbps_find_pkg_in_array(pkgs, curpkgname, NULL))) {
xbps_dictionary_set_bool(reppkgd,
"automatic-install", instd_auto);
xbps_dictionary_set_bool(reppkgd,
"skip-obsoletes", true);
xbps_array_replace_dict_by_name(unsorted,
xbps_array_replace_dict_by_name(pkgs,
reppkgd, curpkgname);
continue;
}
@ -129,7 +125,7 @@ xbps_transaction_package_replace(struct xbps_handle *xhp)
*/
xbps_dictionary_set_cstring_nocopy(instd,
"transaction", "remove");
xbps_array_add(unsorted, instd);
xbps_array_add(pkgs, instd);
free(curpkgname);
}
xbps_object_iterator_release(iter);

View File

@ -42,13 +42,13 @@
*/
static bool
check_virtual_pkgs(struct xbps_handle *xhp,
xbps_array_t pkgs,
xbps_dictionary_t trans_pkgd,
xbps_dictionary_t rev_pkgd)
{
xbps_array_t unsorted, provides;
xbps_array_t provides;
bool matched = false;
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
provides = xbps_dictionary_get(trans_pkgd, "provides");
for (unsigned int i = 0; i < xbps_array_count(provides); i++) {
xbps_array_t rundeps, mdeps;
@ -90,7 +90,7 @@ check_virtual_pkgs(struct xbps_handle *xhp,
xbps_dictionary_get_cstring_nocopy(trans_pkgd, "pkgver", &pkgver);
pkgdepname = xbps_pkg_name(pkgver);
assert(pkgdepname);
if (xbps_find_pkg_in_array(unsorted, pkgdepname, NULL)) {
if (xbps_find_pkg_in_array(pkgs, pkgdepname, NULL)) {
free(pkgdepname);
continue;
}
@ -113,18 +113,16 @@ check_virtual_pkgs(struct xbps_handle *xhp,
}
void HIDDEN
xbps_transaction_revdeps(struct xbps_handle *xhp)
xbps_transaction_revdeps(struct xbps_handle *xhp, xbps_array_t pkgs)
{
xbps_array_t mdeps, unsorted, pkgrdeps, rundeps;
xbps_array_t mdeps, pkgrdeps, rundeps;
xbps_dictionary_t revpkgd;
xbps_object_t obj;
const char *pkgver, *curdep, *revpkgver, *curpkgver, *tract;
char *pkgname, *curdepname, *curpkgname, *str;
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
for (unsigned int i = 0; i < xbps_array_count(unsorted); i++) {
obj = xbps_array_get(unsorted, i);
for (unsigned int i = 0; i < xbps_array_count(pkgs); i++) {
obj = xbps_array_get(pkgs, i);
/*
* Only check packages in transaction being updated.
*/
@ -163,7 +161,7 @@ xbps_transaction_revdeps(struct xbps_handle *xhp)
/*
* First try to match any supported virtual package.
*/
if (check_virtual_pkgs(xhp, obj, revpkgd))
if (check_virtual_pkgs(xhp, pkgs, obj, revpkgd))
continue;
/*
* Try to match real dependencies.
@ -200,7 +198,7 @@ xbps_transaction_revdeps(struct xbps_handle *xhp)
* is in the transaction.
*/
pkgname = xbps_pkg_name(curpkgver);
if (xbps_find_pkg_in_array(unsorted, pkgname, NULL)) {
if (xbps_find_pkg_in_array(pkgs, pkgname, NULL)) {
free(pkgname);
continue;
}

View File

@ -43,9 +43,9 @@
* Abort transaction if such case is found.
*/
static bool
shlib_trans_matched(struct xbps_handle *xhp, const char *pkgver, const char *shlib)
shlib_trans_matched(xbps_array_t pkgs, const char *pkgver, const char *shlib)
{
xbps_array_t unsorted, shrequires;
xbps_array_t shrequires;
xbps_dictionary_t pkgd;
const char *tract;
char *pkgname;
@ -53,8 +53,7 @@ shlib_trans_matched(struct xbps_handle *xhp, const char *pkgver, const char *shl
pkgname = xbps_pkg_name(pkgver);
assert(pkgname);
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
if ((pkgd = xbps_find_pkg_in_array(unsorted, pkgname, NULL)) == NULL) {
if ((pkgd = xbps_find_pkg_in_array(pkgs, pkgname, NULL)) == NULL) {
free(pkgname);
return false;
}
@ -72,7 +71,7 @@ shlib_trans_matched(struct xbps_handle *xhp, const char *pkgver, const char *shl
}
static bool
shlib_matched(struct xbps_handle *xhp, xbps_array_t unsorted, xbps_array_t mshlibs,
shlib_matched(struct xbps_handle *xhp, xbps_array_t pkgs, xbps_array_t mshlibs,
const char *pkgver, const char *shlib)
{
xbps_array_t revdeps;
@ -106,7 +105,7 @@ shlib_matched(struct xbps_handle *xhp, xbps_array_t unsorted, xbps_array_t mshli
* First check if this revdep has been queued in transaction;
* otherwise process the current installed pkg.
*/
pkgd = xbps_find_pkg_in_array(unsorted, rpkgname, NULL);
pkgd = xbps_find_pkg_in_array(pkgs, rpkgname, NULL);
free(rpkgname);
if (pkgd) {
/*
@ -137,7 +136,7 @@ shlib_matched(struct xbps_handle *xhp, xbps_array_t unsorted, xbps_array_t mshli
* installed pkg; find out if there's an update
* in the transaction with the matching version.
*/
if (!shlib_trans_matched(xhp, rpkgver, shlib)) {
if (!shlib_trans_matched(pkgs, rpkgver, shlib)) {
char *buf;
/* shlib not matched */
buf = xbps_xasprintf("%s breaks `%s' "
@ -159,21 +158,17 @@ shlib_matched(struct xbps_handle *xhp, xbps_array_t unsorted, xbps_array_t mshli
bool HIDDEN
xbps_transaction_shlibs(struct xbps_handle *xhp)
xbps_transaction_shlibs(struct xbps_handle *xhp, xbps_array_t pkgs, xbps_array_t mshlibs)
{
xbps_array_t unsorted, mshlibs;
bool unmatched = false;
mshlibs = xbps_dictionary_get(xhp->transd, "missing_shlibs");
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
for (unsigned int i = 0; i < xbps_array_count(unsorted); i++) {
for (unsigned int i = 0; i < xbps_array_count(pkgs); i++) {
xbps_array_t shprovides;
xbps_object_t obj, pkgd;
const char *pkgver, *tract;
char *pkgname;
obj = xbps_array_get(unsorted, i);
obj = xbps_array_get(pkgs, i);
/*
* If pkg does not have 'shlib-provides' obj, pass to next one.
*/
@ -206,7 +201,7 @@ xbps_transaction_shlibs(struct xbps_handle *xhp)
* Check that all shlibs provided by this pkg are used by
* its revdeps.
*/
if (!shlib_matched(xhp, unsorted, mshlibs, pkgver, shlib))
if (!shlib_matched(xhp, pkgs, mshlibs, pkgver, shlib))
unmatched = true;
}
}

View File

@ -1,392 +0,0 @@
/*-
* Copyright (c) 2009-2014 Juan Romero Pardines.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "xbps_api_impl.h"
/*
* Sorting algorithm for packages in the transaction dictionary.
* The transaction dictionary contains all package dictionaries found from
* the repository plist index file in the "unsorted_deps" array.
*
* Any package in the unsorted_deps array is added into the tail and
* later if a package dependency has an index greater than current
* package, the package dependency is moved just before it.
*
* Once that all package dependencies for a package are in the correct
* index, the counter is increased. When all packages in the "unsorted_deps"
* array are processed the loop is stopped and the counter should have
* the same number than the "unsorted_deps" array... otherwise something
* went wrong.
*/
struct pkgdep {
TAILQ_ENTRY(pkgdep) pkgdep_entries;
xbps_dictionary_t d;
char *name;
};
static TAILQ_HEAD(pkgdep_head, pkgdep) pkgdep_list =
TAILQ_HEAD_INITIALIZER(pkgdep_list);
static struct pkgdep *
pkgdep_find(const char *pkg)
{
struct pkgdep *pd = NULL;
const char *pkgver, *tract;
TAILQ_FOREACH(pd, &pkgdep_list, pkgdep_entries) {
if (pd->d == NULL) {
/* ignore entries without dictionary */
continue;
}
xbps_dictionary_get_cstring_nocopy(pd->d,
"transaction", &tract);
/* ignore pkgs to be removed */
if (strcmp(tract, "remove") == 0)
continue;
/* simple match */
xbps_dictionary_get_cstring_nocopy(pd->d, "pkgver", &pkgver);
if (strcmp(pkgver, pkg) == 0)
return pd;
/* pkg expression match */
if (xbps_pkgpattern_match(pkgver, pkg))
return pd;
/* virtualpkg expression match */
if (xbps_match_virtual_pkg_in_dict(pd->d, pkg))
return pd;
}
/* not found */
return NULL;
}
static int32_t
pkgdep_find_idx(const char *pkg)
{
struct pkgdep *pd;
int32_t idx = 0;
const char *pkgver, *tract;
TAILQ_FOREACH(pd, &pkgdep_list, pkgdep_entries) {
if (pd->d == NULL) {
/* ignore entries without dictionary */
idx++;
continue;
}
xbps_dictionary_get_cstring_nocopy(pd->d,
"transaction", &tract);
/* ignore pkgs to be removed */
if (strcmp(tract, "remove") == 0) {
idx++;
continue;
}
/* simple match */
xbps_dictionary_get_cstring_nocopy(pd->d, "pkgver", &pkgver);
if (strcmp(pkgver, pkg) == 0)
return idx;
/* pkg expression match */
if (xbps_pkgpattern_match(pkgver, pkg))
return idx;
/* virtualpkg expression match */
if (xbps_match_virtual_pkg_in_dict(pd->d, pkg))
return idx;
idx++;
}
/* not found */
return -1;
}
static void
pkgdep_release(struct pkgdep *pd)
{
free(pd->name);
free(pd);
}
static struct pkgdep *
pkgdep_alloc(xbps_dictionary_t d, const char *pkg)
{
struct pkgdep *pd;
pd = malloc(sizeof(*pd));
assert(pd);
pd->d = d;
pd->name = strdup(pkg);
return pd;
}
static void
pkgdep_end(xbps_array_t sorted)
{
struct pkgdep *pd;
while ((pd = TAILQ_FIRST(&pkgdep_list)) != NULL) {
TAILQ_REMOVE(&pkgdep_list, pd, pkgdep_entries);
if (sorted != NULL && pd->d != NULL)
xbps_array_add(sorted, pd->d);
pkgdep_release(pd);
}
}
static int
sort_pkg_rundeps(struct xbps_handle *xhp,
struct pkgdep *pd,
xbps_array_t pkg_rundeps,
xbps_array_t unsorted)
{
xbps_array_t provides;
xbps_dictionary_t curpkgd;
struct pkgdep *lpd, *pdn;
const char *str;
int32_t pkgdepidx, curpkgidx;
uint32_t i, idx = 0;
int rv = 0;
xbps_dbg_printf_append(xhp, "\n");
curpkgidx = pkgdep_find_idx(pd->name);
again:
for (i = idx; i < xbps_array_count(pkg_rundeps); i++) {
xbps_array_get_cstring_nocopy(pkg_rundeps, i, &str);
xbps_dbg_printf(xhp, " Required dependency '%s': ", str);
pdn = pkgdep_find(str);
if ((pdn == NULL) && xbps_pkg_is_installed(xhp, str)) {
/*
* Package dependency is installed, just add to
* the list but just mark it as "installed", to avoid
* calling xbps_pkg_is_installed(),
* which is expensive.
*/
xbps_dbg_printf_append(xhp, "installed.\n");
lpd = pkgdep_alloc(NULL, str);
TAILQ_INSERT_TAIL(&pkgdep_list, lpd, pkgdep_entries);
continue;
} else if (pdn != NULL && pdn->d == NULL) {
/*
* Package was added previously into the list
* and is installed, skip.
*/
xbps_dbg_printf_append(xhp, "installed.\n");
continue;
}
if (((curpkgd = xbps_find_pkg_in_array(unsorted, str, "remove")) == NULL) &&
((curpkgd = xbps_find_virtualpkg_in_array(xhp, unsorted, str, "remove")) == NULL)) {
xbps_dbg_printf_append(xhp, "cannot find %s in unsorted array\n", str);
rv = EINVAL;
break;
}
if (pd->d) {
provides = xbps_dictionary_get(pd->d, "provides");
if (provides && xbps_match_virtual_pkg_in_array(provides, str)) {
xbps_dbg_printf_append(xhp, "%s is a vpkg provided by %s, ignored.\n", str, pd->name);
continue;
}
}
lpd = pkgdep_alloc(curpkgd, str);
if (pdn == NULL) {
/*
* If package is not in the list, add to the tail
* and iterate at the same position.
*/
TAILQ_INSERT_TAIL(&pkgdep_list, lpd, pkgdep_entries);
idx = i;
xbps_dbg_printf_append(xhp, "added into the tail, "
"checking again...\n");
goto again;
}
/*
* Find package dependency index.
*/
pkgdepidx = pkgdep_find_idx(str);
/*
* If package dependency index is less than current
* package index, it's already sorted.
*/
if (pkgdepidx < curpkgidx) {
xbps_dbg_printf_append(xhp, "already sorted.\n");
pkgdep_release(lpd);
} else if (pd == pdn) {
/*
* Processing same pkg, just continue.
*/
xbps_dbg_printf_append(xhp, "already sorted.\n");
pkgdep_release(lpd);
} else {
/*
* Remove package dependency from list and move
* it before current package.
*/
TAILQ_REMOVE(&pkgdep_list, pdn, pkgdep_entries);
pkgdep_release(pdn);
TAILQ_INSERT_BEFORE(pd, lpd, pkgdep_entries);
xbps_dbg_printf_append(xhp,
"added before `%s'.\n", pd->name);
}
}
return rv;
}
int HIDDEN
xbps_transaction_sort(struct xbps_handle *xhp)
{
xbps_array_t provides, sorted, unsorted, rundeps;
xbps_object_t obj;
struct pkgdep *pd;
unsigned int ndeps = 0, cnt = 0;
const char *pkgver, *tract, *vpkgdep;
int rv = 0;
bool vpkg_found;
if ((sorted = xbps_array_create()) == NULL)
return ENOMEM;
/*
* Add sorted packages array into transaction dictionary (empty).
*/
if (!xbps_dictionary_set(xhp->transd, "packages", sorted)) {
rv = EINVAL;
goto out;
}
/*
* All required deps are satisfied (already installed).
*/
unsorted = xbps_dictionary_get(xhp->transd, "unsorted_deps");
if (xbps_array_count(unsorted) == 0) {
xbps_dictionary_set(xhp->transd, "packages", sorted);
xbps_object_release(sorted);
return 0;
}
/*
* The sorted array should have the same capacity than
* all objects in the unsorted array.
*/
ndeps = xbps_array_count(unsorted);
/*
* Iterate over the unsorted package dictionaries and sort all
* its package dependencies.
*/
for (unsigned int i = 0; i < ndeps; i++) {
vpkg_found = false;
obj = xbps_array_get(unsorted, i);
xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver);
xbps_dictionary_get_cstring_nocopy(obj, "transaction", &tract);
provides = xbps_dictionary_get(obj, "provides");
xbps_dbg_printf(xhp, "Sorting package '%s' (%s): ", pkgver, tract);
if (provides) {
/*
* If current pkgdep provides any virtual pkg check
* if any of them was previously added. If true, don't
* add it into the list again, just order its deps.
*/
for (unsigned int j = 0; j < xbps_array_count(provides); j++) {
xbps_array_get_cstring_nocopy(provides,
j, &vpkgdep);
pd = pkgdep_find(vpkgdep);
if (pd != NULL) {
xbps_dbg_printf_append(xhp, "already "
"sorted via `%s' vpkg.", vpkgdep);
vpkg_found = true;
break;
}
}
}
pd = pkgdep_find(pkgver);
if ((!strcmp(tract, "remove") || (!pd && !vpkg_found))) {
/*
* If package not in list, just add to the tail.
*/
pd = pkgdep_alloc(obj, pkgver);
if (pd == NULL) {
pkgdep_end(NULL);
rv = ENOMEM;
goto out;
}
if (strcmp(tract, "remove") == 0) {
xbps_dbg_printf_append(xhp, "added into head.\n");
TAILQ_INSERT_HEAD(&pkgdep_list, pd,
pkgdep_entries);
cnt++;
continue;
} else {
xbps_dbg_printf_append(xhp, "added into tail.");
TAILQ_INSERT_TAIL(&pkgdep_list, pd,
pkgdep_entries);
}
}
if (pd == NULL)
continue;
/*
* Packages that don't have deps go at head, because
* it doesn't matter.
*/
rundeps = xbps_dictionary_get(obj, "run_depends");
if (xbps_array_count(rundeps) == 0) {
xbps_dbg_printf_append(xhp, "\n");
cnt++;
continue;
}
/*
* Sort package run-time dependencies for this package.
*/
if ((rv = sort_pkg_rundeps(xhp, pd, rundeps, unsorted)) != 0) {
pkgdep_end(NULL);
goto out;
}
cnt++;
}
/*
* We are done, now we have to copy all pkg dictionaries
* from the sorted list into the "packages" array, and at
* the same time freeing memory used for temporary sorting.
*/
pkgdep_end(sorted);
/*
* Sanity check that the array contains the same number of
* objects than the total number of required dependencies.
*/
assert(cnt == xbps_array_count(unsorted));
/*
* We are done, all packages were sorted... remove the
* temporary array with unsorted packages.
*/
xbps_dictionary_remove(xhp->transd, "unsorted_deps");
out:
if (rv != 0)
xbps_dictionary_remove(xhp->transd, "packages");
xbps_object_release(sorted);
return rv;
}

View File

@ -80,7 +80,7 @@ install_with_vpkg_deps_body() {
atf_check_equal $? 0
cd ..
echo -e "C-1.0_1\nA-1.0_1\nB-1.0_1\nD-1.0_1\n" > exp
echo -e "A-1.0_1\nB-1.0_1\nD-1.0_1\nC-1.0_1\n" > exp
xbps-install -C empty.conf -r root --repository=$PWD/some_repo -yn E|awk '{print $1}' > out
echo >> out
echo "exp: '$(cat exp)'" >&2