diff --git a/doc/TODO b/doc/TODO index 847c6985f3..85be0de6e5 100644 --- a/doc/TODO +++ b/doc/TODO @@ -32,7 +32,3 @@ xbps-repo: create the file from scratch, for performance. * Some problems when installing binpkgs with repos: - If first repo is valid but second is not, will fail. - -lib: - * Dependency code has a problem yet: fix the order of dependencies with - the same priority, as seen by xbps-base-{chroot,system}. diff --git a/doc/dependency_chain.plist b/doc/dependency_chain.plist deleted file mode 100644 index 7cb8677bd6..0000000000 --- a/doc/dependency_chain.plist +++ /dev/null @@ -1,84 +0,0 @@ - - -- Dependency chain for a package -- - - The order to which dependencies are installed is: - - 1- Indirect deps with high->low priority. - 2- Direct deps with high->low priority. - - Priority is increased by one any time a packages depends on, so a - package that is needed by 30 packages will be installed before - one that is needed by 10 packages. - - - - missing_deps - - - pkgname - blurb - version - 2.1 - - - - - - installed_deps - - - pkgname - blah - version - 1.2 - requiredby - foo-2.0 - - ... - - - - - direct_deps - - - pkgname - blob - version - 2.0 - repository - /blah/foo - requiredby - foo-2.0 - priority - 100 - filename - blob-2.0.i686.xbps - - ... - - - - - indirect_deps - - - pkgname - X - version - 1.0 - repository - /blah/foo - requiredby - blob-2.0 - priority - 90 - filename - X-1.0.i686.xbps - - ... - - diff --git a/include/install.h b/include/install.h index 101e3307df..4cd19aadb3 100644 --- a/include/install.h +++ b/include/install.h @@ -27,16 +27,19 @@ #define _XBPS_INSTALL_H_ /* From lib/install.c, lib/depends.c and lib/unpack.c */ -int xbps_install_pkg_deps(prop_dictionary_t, const char *); -int xbps_install_binary_pkg(const char *, const char *); -int xbps_install_binary_pkg_fini(prop_dictionary_t, prop_dictionary_t, - const char *); -int xbps_register_pkg(prop_dictionary_t, const char *, const char *, - const char *); -int xbps_unpack_binary_pkg(prop_dictionary_t, prop_dictionary_t, - const char *, - void (*cb_print)(prop_dictionary_t)); -int xbps_update_pkg_requiredby(prop_array_t, prop_dictionary_t); -int xbps_find_deps_in_pkg(prop_dictionary_t, prop_dictionary_t); +int xbps_install_pkg_deps(prop_dictionary_t, const char *); +int xbps_install_binary_pkg(const char *, const char *); +int xbps_install_binary_pkg_fini(prop_dictionary_t, prop_dictionary_t, + const char *); +int xbps_register_pkg(prop_dictionary_t, const char *, const char *, + const char *); +int xbps_unpack_binary_pkg(prop_dictionary_t, prop_dictionary_t, + const char *, + void (*cb_print)(prop_dictionary_t)); +int xbps_update_pkg_requiredby(prop_array_t, prop_dictionary_t); +int xbps_find_deps_in_pkg(prop_dictionary_t, prop_dictionary_t); + +/* From lib/sortdeps.c */ +int xbps_sort_pkg_deps(prop_dictionary_t); #endif /* !_XBPS_INSTALL_H_ */ diff --git a/lib/Makefile b/lib/Makefile index 96677ddc81..0c065e9deb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -10,7 +10,7 @@ LIBXBPS_LDFLAGS = -larchive -lprop -shared -Wl,-soname,$(LIBXBPS).$(MAJOR) OBJECTS = cmpver.o depends.o fexec.o humanize_number.o install.o OBJECTS += plist.o remove.o repository.o requiredby.o sha256.o -OBJECTS += unpack.o util.o +OBJECTS += sortdeps.o unpack.o util.o all: $(LIBXBPS) .PHONY: all diff --git a/lib/depends.c b/lib/depends.c index b5043aedb6..ac7fe79a4b 100644 --- a/lib/depends.c +++ b/lib/depends.c @@ -44,7 +44,7 @@ static prop_dictionary_t chaindeps; static int create_deps_dictionary(void) { - prop_array_t installed, direct, indirect, missing; + prop_array_t installed, unsorted, missing; int rv = 0; chaindeps = prop_dictionary_create(); @@ -63,40 +63,28 @@ create_deps_dictionary(void) goto fail2; } - direct = prop_array_create(); - if (direct == NULL) { + unsorted = prop_array_create(); + if (unsorted == NULL) { rv = ENOMEM; goto fail3; } - indirect = prop_array_create(); - if (indirect == NULL) { - rv = ENOMEM; - goto fail4; - } - if (!xbps_add_obj_to_dict(chaindeps, missing, "missing_deps")) { rv = EINVAL; - goto fail5; + goto fail4; } if (!xbps_add_obj_to_dict(chaindeps, installed, "installed_deps")) { rv = EINVAL; - goto fail5; + goto fail4; } - if (!xbps_add_obj_to_dict(chaindeps, direct, "direct_deps")) { + if (!xbps_add_obj_to_dict(chaindeps, unsorted, "unsorted_deps")) { rv = EINVAL; - goto fail5; - } - if (!xbps_add_obj_to_dict(chaindeps, indirect, "indirect_deps")) { - rv = EINVAL; - goto fail5; + goto fail4; } return rv; -fail5: - prop_object_release(indirect); fail4: - prop_object_release(direct); + prop_object_release(unsorted); fail3: prop_object_release(installed); fail2: @@ -111,14 +99,16 @@ static int store_dependency(prop_dictionary_t origind, prop_dictionary_t depd, prop_dictionary_t repod) { - prop_dictionary_t dict, curpkgdir, curpkgindir; - prop_array_t array, rundeps_array; + prop_dictionary_t dict, curdict; + prop_array_t array, rundeps_array, reqby_array; + prop_string_t reqbystr; uint32_t prio = 0; - size_t len = 0; + size_t len = 0, dirdepscnt = 0, indirdepscnt = 0; const char *pkgname, *version, *reqbyname, *reqbyver; const char *repoloc, *binfile, *array_key, *originpkg, *short_desc; char *reqby; int rv = 0; + bool indirectdep = false; assert(origind != NULL); assert(depd != NULL); @@ -142,6 +132,7 @@ store_dependency(prop_dictionary_t origind, prop_dictionary_t depd, return ENOMEM; (void)snprintf(reqby, len, "%s-%s", reqbyname, reqbyver); + reqbystr = prop_string_create_cstring(reqby); /* * Check if dependency is already installed to select the @@ -158,40 +149,37 @@ store_dependency(prop_dictionary_t origind, prop_dictionary_t depd, } else { /* * Required dependency is not installed. Check if it's - * already registered in the chain, and update priority + * already registered in the chain, and update some objects * or add the object into array otherwise. */ - prop_dictionary_get_cstring_nocopy(chaindeps, "origin", - &originpkg); - curpkgdir = xbps_find_pkg_in_dict(chaindeps, - "direct_deps", pkgname); - curpkgindir = xbps_find_pkg_in_dict(chaindeps, - "indirect_deps", pkgname); - - if (strcmp(originpkg, reqbyname) == 0) - array_key = "direct_deps"; - else - array_key = "indirect_deps"; - - if (curpkgdir && curpkgindir) { - goto out; - - } else if (curpkgdir) { - /* - * Update the priority. - */ - prop_dictionary_get_uint32(curpkgdir, - "priority", &prio); - prop_dictionary_set_uint32(curpkgdir, - "priority", ++prio); - goto out; - } else if (curpkgindir) { - prop_dictionary_get_uint32(curpkgindir, - "priority", &prio); - prop_dictionary_set_uint32(curpkgindir, - "priority", ++prio); + array_key = "unsorted_deps"; + prop_dictionary_get_cstring_nocopy(chaindeps, + "origin", &originpkg); + curdict = xbps_find_pkg_in_dict(chaindeps, array_key, pkgname); + /* + * Update priority and required_by objects. + */ + if (curdict) { + prop_dictionary_get_uint32(curdict, "priority", &prio); + prop_dictionary_set_uint32(curdict, "priority", ++prio); + reqby_array = prop_dictionary_get(curdict, + "required_by"); + if (!xbps_find_string_in_array(reqby_array, reqby)) + prop_array_add(reqby_array, reqbystr); goto out; } + if (strcmp(originpkg, reqbyname)) { + indirectdep = true; + prop_dictionary_get_uint32(chaindeps, + "indirectdeps_count", &indirdepscnt); + prop_dictionary_set_uint32(chaindeps, + "indirectdeps_count", ++indirdepscnt); + } else { + prop_dictionary_get_uint32(chaindeps, + "directdeps_count", &dirdepscnt); + prop_dictionary_set_uint32(chaindeps, + "directdeps_count", ++dirdepscnt); + } } /* @@ -215,17 +203,25 @@ store_dependency(prop_dictionary_t origind, prop_dictionary_t depd, */ prop_dictionary_set_cstring(dict, "pkgname", pkgname); prop_dictionary_set_cstring(dict, "version", version); - prop_dictionary_set_cstring(dict, "requiredby", reqby); rundeps_array = prop_dictionary_get(depd, "run_depends"); if (rundeps_array && prop_array_count(rundeps_array) > 0) prop_dictionary_set(dict, "run_depends", rundeps_array); - if ((strcmp(array_key, "direct_deps") == 0) || - (strcmp(array_key, "indirect_deps") == 0)) { + reqby_array = prop_array_create(); + if (reqby_array == NULL) { + prop_object_release(dict); + rv = ENOMEM; + goto out; + } + prop_array_add(reqby_array, reqbystr); + prop_dictionary_set(dict, "required_by", reqby_array); + + if (strcmp(array_key, "unsorted_deps") == 0) { prop_dictionary_set_cstring(dict, "repository", repoloc); prop_dictionary_set_cstring(dict, "filename", binfile); prop_dictionary_set_uint32(dict, "priority", prio); prop_dictionary_set_cstring(dict, "short_desc", short_desc); + prop_dictionary_set_bool(dict, "indirect_dep", indirectdep); } /* * Add the dictionary into the array. @@ -238,6 +234,7 @@ store_dependency(prop_dictionary_t origind, prop_dictionary_t depd, out: free(reqby); + prop_object_release(reqbystr); return rv; } @@ -252,6 +249,9 @@ add_missing_reqdep(const char *pkgname, const char *version) assert(pkgname != NULL); assert(version != NULL); + /* + * Adds a package into the missing deps array. + */ if (check_missing_reqdep(pkgname, version, &idx) == 0) return EEXIST; @@ -292,6 +292,9 @@ check_missing_reqdep(const char *pkgname, const char *version, if (iter == NULL) return ENOMEM; + /* + * Finds the index of a package in the missing deps array. + */ while ((obj = prop_object_iterator_next(iter)) != NULL) { prop_dictionary_get_cstring_nocopy(obj, "pkgname", &missname); prop_dictionary_get_cstring_nocopy(obj, "version", &missver); @@ -413,101 +416,39 @@ out: int xbps_install_pkg_deps(prop_dictionary_t pkg, const char *destdir) { - prop_array_t array, installed, direct, indirect; - prop_dictionary_t dict; + prop_array_t required; prop_object_t obj; prop_object_iterator_t iter; - uint32_t maxprio = 0, prio = 0; - size_t curidx = 0, idx = 0; - const char *array_key, *reqby, *curname, *curver; int rv = 0; assert(pkg != NULL); /* - * Install required dependencies of a package. - * The order for installation will be: - * - * - Indirect deps with high->low prio. - * - Direct deps with high->low prio. + * Sort the dependency chain into an array. */ + if ((rv = xbps_sort_pkg_deps(chaindeps)) != 0) + goto out; - /* - * First case: all deps are satisfied. - */ - installed = prop_dictionary_get(chaindeps, "installed_deps"); - direct = prop_dictionary_get(chaindeps, "direct_deps"); - indirect = prop_dictionary_get(chaindeps, "indirect_deps"); - if (prop_array_count(direct) == 0 && prop_array_count(indirect) == 0 && - prop_array_count(installed) > 0) + required = prop_dictionary_get(chaindeps, "required_deps"); + if (required == NULL) return 0; - /* - * Second case: only direct deps are required. - */ - if (prop_array_count(indirect) == 0 && prop_array_count(direct) > 0) - array_key = "direct_deps"; - else - array_key = "indirect_deps"; - -again: - array = prop_dictionary_get(chaindeps, array_key); - if (array && prop_array_count(array) == 0) { - rv = 0; - goto out; - } else if (array == NULL || prop_array_count(array) == 0) { - rv = EINVAL; - goto out; - } - - iter = prop_array_iterator(array); + iter = prop_array_iterator(required); if (iter == NULL) { rv = ENOMEM; goto out; } + /* + * Install all required dependencies, previously sorted. + */ while ((obj = prop_object_iterator_next(iter)) != NULL) { - prop_dictionary_get_uint32(obj, "priority", &prio); - if (maxprio < prio) { - curidx = idx; - maxprio = prio; - } - idx++; + rv = xbps_install_binary_pkg_fini(NULL, obj, destdir); + if (rv != 0) + goto out; } prop_object_iterator_release(iter); - dict = prop_array_get(array, curidx); - if (dict == NULL) { - rv = ENOENT; - goto out; - } - - prop_dictionary_get_cstring_nocopy(dict, "pkgname", &curname); - prop_dictionary_get_cstring_nocopy(dict, "version", &curver); - prop_dictionary_get_cstring_nocopy(dict, "requiredby", &reqby); - - printf("Installing %s-%s required by %s...\n", curname, curver, reqby); - rv = xbps_install_binary_pkg_fini(NULL, dict, destdir); - if (rv != 0) { - printf("Error while installing %s-%s (%s)\n", curname, curver, - strerror(rv)); - goto out; - } - - prop_array_remove(array, curidx); - if (prop_array_count(array) > 0) { - prio = maxprio = 0; - curidx = idx = 0; - goto again; - } else { - prio = maxprio = 0; - curidx = idx = 0; - array_key = "direct_deps"; - goto again; - } - out: - prop_object_release(chaindeps); - return rv; } diff --git a/lib/sortdeps.c b/lib/sortdeps.c new file mode 100644 index 0000000000..6983fd725c --- /dev/null +++ b/lib/sortdeps.c @@ -0,0 +1,316 @@ +/*- + * Copyright (c) 2009 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 +#include +#include +#include +#include +#include + +#include + +struct sorted_dependency { + TAILQ_ENTRY(sorted_dependency) chain; + prop_dictionary_t dict; + size_t idx; + ssize_t newidx; + bool unsorted; + bool reorg; +}; + +static TAILQ_HEAD(sdep_head, sorted_dependency) sdep_list = + TAILQ_HEAD_INITIALIZER(sdep_list); + +static ssize_t +find_pkgdict_with_highest_prio(prop_array_t array, uint32_t *maxprio, + bool do_indirect) +{ + prop_object_t obj; + prop_object_iterator_t iter; + uint32_t prio = 0; + size_t idx = 0; + ssize_t curidx = -1; + bool indirect; + + assert(array != NULL); + + iter = prop_array_iterator(array); + if (iter == NULL) { + errno = ENOMEM; + return -1; + } + + /* + * Finds the index of a package with the highest priority. + */ + while ((obj = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_uint32(obj, "priority", &prio); + prop_dictionary_get_bool(obj, "indirect_dep", &indirect); + + if (do_indirect) { + if ((*maxprio <= prio) && indirect) { + curidx = idx; + *maxprio = prio; + } + } else { + if ((*maxprio <= prio) && !indirect) { + curidx = idx; + *maxprio = prio; + } + } + idx++; + } + prop_object_iterator_release(iter); + + if (curidx == -1) + errno = ENOENT; + + return curidx; +} + +static struct sorted_dependency * +find_sorteddep_by_name(const char *pkgname) +{ + struct sorted_dependency *sdep; + const char *curname; + + TAILQ_FOREACH(sdep, &sdep_list, chain) { + prop_dictionary_get_cstring_nocopy(sdep->dict, + "pkgname", &curname); + if (strcmp(pkgname, curname) == 0) + break; + } + + return sdep; +} + +int +xbps_sort_pkg_deps(prop_dictionary_t chaindeps) +{ + prop_array_t installed, sorted, unsorted, rundeps_array; + prop_dictionary_t dict; + prop_object_t obj; + prop_object_iterator_t iter; + struct sorted_dependency *sdep, *sdep2; + uint32_t maxprio = 0; + size_t curidx = 0, indirdepscnt = 0, dirdepscnt = 0, cnt = 0; + const char *curpkg, *rundep; + char *pkgname; + int rv = 0; + + assert(chaindeps != NULL); + + sorted = prop_array_create(); + if (sorted == NULL) + return ENOMEM; + + /* + * All required deps are satisfied (already installed). + */ + installed = prop_dictionary_get(chaindeps, "installed_deps"); + unsorted = prop_dictionary_get(chaindeps, "unsorted_deps"); + if (prop_array_count(unsorted) == 0 && prop_array_count(installed) > 0) + return 0; + + prop_dictionary_get_uint32(chaindeps, "indirectdeps_count", + &indirdepscnt); + prop_dictionary_get_uint32(chaindeps, "directdeps_count", + &dirdepscnt); + unsorted = prop_dictionary_get(chaindeps, "unsorted_deps"); + /* + * Pass 1: order indirect deps by priority. + */ + while (cnt < indirdepscnt) { + curidx = find_pkgdict_with_highest_prio(unsorted, + &maxprio, true); + if (curidx == -1) { + rv = errno; + goto out; + } + dict = prop_array_get(unsorted, curidx); + if (dict == NULL) { + rv = errno; + goto out; + } + sdep = calloc(1, sizeof(*sdep)); + if (sdep == NULL) { + rv = ENOMEM; + goto out; + } + sdep->dict = prop_dictionary_copy(dict); + sdep->idx = cnt; + sdep->newidx = -1; + TAILQ_INSERT_TAIL(&sdep_list, sdep, chain); + prop_array_remove(unsorted, curidx); + maxprio = 0; + cnt++; + } + + cnt = 0; + /* + * Pass 2: order direct deps by priority. + */ + while (cnt < dirdepscnt) { + curidx = find_pkgdict_with_highest_prio(unsorted, + &maxprio, false); + if (curidx == -1) { + rv = errno; + goto out; + } + dict = prop_array_get(unsorted, curidx); + if (dict == NULL) { + rv = errno; + goto out; + } + sdep = calloc(1, sizeof(*sdep)); + if (sdep == NULL) { + rv = ENOMEM; + goto out; + } + sdep->dict = prop_dictionary_copy(dict); + sdep->idx = cnt + indirdepscnt; + sdep->newidx = -1; + TAILQ_INSERT_TAIL(&sdep_list, sdep, chain); + prop_array_remove(unsorted, curidx); + maxprio = 0; + cnt++; + } + + /* + * Pass 3: update new index position by looking at run_depends and + * its current index position. + */ + TAILQ_FOREACH(sdep, &sdep_list, chain) { + prop_dictionary_get_cstring_nocopy(sdep->dict, + "pkgname", &curpkg); + rundeps_array = prop_dictionary_get(sdep->dict, "run_depends"); + if (rundeps_array == NULL) + continue; + + iter = prop_array_iterator(rundeps_array); + if (iter == NULL) { + rv = ENOMEM; + goto out; + } + curidx = sdep->idx; + + while ((obj = prop_object_iterator_next(iter)) != NULL) { + rundep = prop_string_cstring_nocopy(obj); + pkgname = xbps_get_pkg_name(rundep); + /* + * If package is installed, pass to the next one. + */ + if (xbps_check_is_installed_pkgname(pkgname)) { + free(pkgname); + continue; + } + /* Ignore itself */ + if (strcmp(curpkg, pkgname) == 0) { + free(pkgname); + continue; + } + + sdep2 = find_sorteddep_by_name(pkgname); + free(pkgname); + /* + * If required dependency is before current package, + * pass to the next one. + */ + if (curidx > sdep2->idx) + continue; + /* + * Update index position for the two objects. + */ + if (!sdep2->unsorted) { + sdep2->unsorted = true; + sdep2->newidx = curidx; + sdep->newidx = curidx + 1; + } + } + prop_object_iterator_release(iter); + } + prop_dictionary_remove(chaindeps, "unsorted_deps"); + + /* + * Pass 4: copy dictionaries into the final array with the + * correct index position for all dependencies. + */ + TAILQ_FOREACH(sdep, &sdep_list, chain) { + if (sdep->reorg) + continue; + + if (sdep->newidx != -1) { + TAILQ_FOREACH(sdep2, &sdep_list, chain) { + if (sdep2->unsorted) { + if (!prop_array_set(sorted, + sdep2->newidx, sdep2->dict)) { + rv = errno; + goto out; + } + sdep2->newidx = -1; + sdep2->unsorted = false; + sdep2->reorg = true; + break; + } + } + if (!prop_array_set(sorted, sdep->newidx, sdep->dict)) { + rv = errno; + goto out; + } + sdep->newidx = -1; + } else { + if (!prop_array_add(sorted, sdep->dict)) { + rv = errno; + goto out; + } + } + } + + /* + * Sanity check that the array contains the same number of + * objects than the total number of required dependencies. + */ + cnt = dirdepscnt + indirdepscnt; + if (cnt != prop_array_count(sorted)) { + rv = EINVAL; + goto out; + } + + if (!prop_dictionary_set(chaindeps, "required_deps", sorted)) + rv = EINVAL; +out: + /* + * Release resources used by temporary sorting. + */ + prop_object_release(sorted); + while ((sdep = TAILQ_FIRST(&sdep_list)) != NULL) { + TAILQ_REMOVE(&sdep_list, sdep, chain); + prop_object_release(sdep->dict); + free(sdep); + } + + return rv; +}