/*- * Copyright (c) 2009-2011 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 "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. * * When a package has no rundeps or all rundeps are satisfied, the package * dictionary is added into the sorted "packages" array and it is marked as * sorted in its dictionary with a boolean object. * * It will loop until all packages are processed and will check that * the number of packages added into the "packages" array is the same than * it was in the "unsorted_deps" array. * * Sure I could make it not iterate so many times but probably it will * also be slower! that's why XBPS uses proplib with its red-black trees, * iterating over them is fast. */ int HIDDEN xbps_sort_pkg_deps(void) { prop_dictionary_t transd; prop_array_t sorted, unsorted, rundeps; prop_object_t obj, obj2; prop_object_iterator_t iter, iter2; size_t ndeps = 0, rundepscnt = 0, cnt = 0; const char *pkgname, *pkgver, *str; char *pkgnamedep; int rv = 0; bool done; if ((transd = xbps_transaction_dictionary_get()) == NULL) return EINVAL; sorted = prop_array_create(); if (sorted == NULL) return ENOMEM; /* * Add sorted packages array into transaction dictionary (empty). */ if (!prop_dictionary_set(transd, "packages", sorted)) { rv = EINVAL; goto out; } /* * All required deps are satisfied (already installed). */ unsorted = prop_dictionary_get(transd, "unsorted_deps"); if (prop_array_count(unsorted) == 0) { prop_dictionary_set(transd, "packages", sorted); return 0; } /* * The sorted array should have the same capacity than * all objects in the unsorted array. */ ndeps = prop_array_count(unsorted); if (!prop_array_ensure_capacity(sorted, ndeps)) { xbps_error_printf("failed to set capacity to the sorted " "pkgdeps array\n"); return ENOMEM; } iter = prop_array_iterator(unsorted); if (iter == NULL) { rv = ENOMEM; goto out; } again: /* * Order all deps by looking at its run_depends array. */ while ((obj = prop_object_iterator_next(iter)) != NULL) { /* no more iterations required, pkglist is sorted */ if (cnt >= ndeps) break; prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); xbps_dbg_printf("Sorting package '%s': ", pkgver); /* * Check if package was sorted previously and skip. */ done = false; prop_dictionary_get_bool(obj, "sorted", &done); if (done) { xbps_dbg_printf_append("skipping, already sorted.\n", pkgname); continue; } /* * Packages that don't have deps go unsorted, because * it doesn't matter. */ rundeps = prop_dictionary_get(obj, "run_depends"); if (rundeps == NULL || prop_array_count(rundeps) == 0) { xbps_dbg_printf_append("added (no rundeps) into " "the sorted queue.\n"); prop_array_add(sorted, obj); prop_dictionary_set_bool(obj, "sorted", true); cnt++; continue; } iter2 = prop_array_iterator(rundeps); if (iter2 == NULL) { rv = ENOMEM; goto out; } xbps_dbg_printf_append("\n"); /* * Iterate over the run_depends array, and find out if they * were already added in the sorted list. */ while ((obj2 = prop_object_iterator_next(iter2)) != NULL) { str = prop_string_cstring_nocopy(obj2); if (str == NULL) { rv = EINVAL; goto out; } pkgnamedep = xbps_get_pkgpattern_name(str); if (pkgnamedep == NULL) { rv = EINVAL; goto out; } xbps_dbg_printf(" Required dependency '%s': ", str); /* * If dependency is already satisfied or queued, * pass to the next one. */ if (xbps_check_is_installed_pkg_by_pattern(str)) { rundepscnt++; xbps_dbg_printf_append("installed.\n"); } else if (xbps_find_pkg_in_dict_by_name(transd, "packages", pkgnamedep)) { xbps_dbg_printf_append("queued.\n"); rundepscnt++; } else { xbps_dbg_printf_append("not installed.\n"); } free(pkgnamedep); } prop_object_iterator_release(iter2); /* Add dependency if all its required deps are already added */ if (prop_array_count(rundeps) == rundepscnt) { prop_array_add(sorted, obj); prop_dictionary_set_bool(obj, "sorted", true); xbps_dbg_printf("Added package '%s' to the sorted " "queue (all rundeps satisfied).\n", pkgver); rundepscnt = 0; cnt++; continue; } xbps_dbg_printf("Unsorted package '%s' has missing " "rundeps (missing %zu).\n", pkgver, prop_array_count(rundeps) - rundepscnt); rundepscnt = 0; } /* Iterate until all deps are processed. */ if (cnt < ndeps) { xbps_dbg_printf("Missing required deps! queued: %zu " "required: %zu.\n", cnt, ndeps); prop_object_iterator_reset(iter); goto again; } prop_object_iterator_release(iter); /* * Sanity check that the array contains the same number of * objects than the total number of required dependencies. */ if (ndeps != prop_array_count(sorted)) { xbps_dbg_printf("wrong sorted deps cnt %zu vs %zu\n", ndeps, prop_array_count(sorted)); rv = EINVAL; goto out; } /* * We are done, all packages were sorted... remove the * temporary array with unsorted packages. */ prop_dictionary_remove(transd, "unsorted_deps"); out: if (rv != 0) prop_dictionary_remove(transd, "packages"); prop_object_release(sorted); return rv; }