2010-11-20 12:29:18 +02:00
|
|
|
/*
|
|
|
|
* prereq.c - Determine prerequisite packages
|
|
|
|
*
|
|
|
|
* Written 2010 by Werner Almesberger
|
|
|
|
* Copyright 2010 Werner Almesberger
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2010-11-19 19:00:15 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
2010-11-22 04:51:35 +02:00
|
|
|
#include "util.h"
|
2010-11-19 19:00:15 +02:00
|
|
|
#include "id.h"
|
2010-11-22 21:19:32 +02:00
|
|
|
#include "pkg.h"
|
2010-11-19 19:00:15 +02:00
|
|
|
#include "qpkg.h"
|
2010-11-20 01:51:49 +02:00
|
|
|
#include "prereq.h"
|
2010-11-19 19:00:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
struct list {
|
|
|
|
const struct ref *refs;
|
|
|
|
struct list *next;
|
|
|
|
};
|
|
|
|
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
struct stack {
|
|
|
|
struct pkg *pkg;
|
|
|
|
struct stack *next;
|
|
|
|
};
|
|
|
|
|
2010-11-19 19:00:15 +02:00
|
|
|
|
|
|
|
static struct pkg **best = NULL;
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
static struct pkg **installs = NULL;
|
2010-11-19 19:00:15 +02:00
|
|
|
static int n_best; /* undefined if best == NULL */
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
static int n_install = 0;
|
|
|
|
static int install_max = 0;
|
2010-11-22 01:55:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* ----- List of packages considered for installation ---------------------- */
|
|
|
|
|
|
|
|
|
2010-11-19 19:00:15 +02:00
|
|
|
static void done(void)
|
|
|
|
{
|
|
|
|
int size;
|
|
|
|
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
if (best && n_best <= n_install)
|
2010-11-19 19:00:15 +02:00
|
|
|
return;
|
|
|
|
free(best);
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
size = sizeof(*installs)*(n_install+1);
|
2010-11-19 19:00:15 +02:00
|
|
|
best = alloc_size(size);
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
memcpy(best, installs, sizeof(*installs)*n_install);
|
|
|
|
n_best = n_install;
|
2010-11-19 19:00:15 +02:00
|
|
|
best[n_best] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
static void append_install(struct pkg *pkg)
|
2010-11-19 19:00:15 +02:00
|
|
|
{
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
if (n_install == install_max) {
|
|
|
|
install_max = (install_max+1)*2;
|
|
|
|
installs = realloc(installs, sizeof(*installs)*install_max);
|
|
|
|
if (!installs) {
|
2010-11-19 19:00:15 +02:00
|
|
|
perror("realloc");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
installs[n_install++] = pkg;
|
2010-11-20 17:25:45 +02:00
|
|
|
assert(!pkg->mark && !(pkg->flags & QPKG_INSTALLED));
|
2010-11-19 19:00:15 +02:00
|
|
|
pkg->mark = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
static void backtrack(void)
|
2010-11-19 19:00:15 +02:00
|
|
|
{
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
assert(n_install);
|
|
|
|
n_install--;
|
|
|
|
installs[n_install]->mark = 0;
|
2010-11-19 19:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-22 01:55:25 +02:00
|
|
|
/* ----- Check dependencies and conflicts ---------------------------------- */
|
|
|
|
|
|
|
|
|
2010-11-19 19:00:15 +02:00
|
|
|
static int satisfies(const struct pkg *pkg, const struct ref *ref)
|
|
|
|
{
|
|
|
|
int cmp;
|
|
|
|
|
|
|
|
if (pkg->id != ref->pkg)
|
|
|
|
return 0;
|
|
|
|
if (!ref->version)
|
|
|
|
return 1;
|
2010-11-22 04:24:44 +02:00
|
|
|
/* @@@ error in the database, not qpkg's fault */
|
2010-11-19 19:00:15 +02:00
|
|
|
assert(pkg->version);
|
|
|
|
cmp = comp_versions(pkg->version, ref->version);
|
|
|
|
//fprintf(stderr, "%.*s <%d> %.*s\n",
|
2010-11-21 08:25:53 +02:00
|
|
|
// ID2PF(pkg->version), cmp, ID2PF(ref->version));
|
2010-11-19 19:00:15 +02:00
|
|
|
switch (ref->relop) {
|
|
|
|
case rel_eq:
|
|
|
|
return !cmp;
|
|
|
|
case rel_ge:
|
|
|
|
return cmp >= 0;
|
|
|
|
case rel_lt:
|
|
|
|
return cmp < 0;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-22 11:52:53 +02:00
|
|
|
static int old_conflicts(const struct pkg *pkg, const struct list *conf)
|
2010-11-19 19:00:15 +02:00
|
|
|
{
|
2010-11-22 04:24:44 +02:00
|
|
|
const struct ref *ref;
|
|
|
|
|
|
|
|
while (conf) {
|
|
|
|
for (ref = conf->refs; ref; ref = ref->next)
|
|
|
|
if (satisfies(pkg, ref))
|
|
|
|
return 1;
|
|
|
|
conf = conf->next;
|
|
|
|
}
|
2010-11-19 19:00:15 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-22 11:52:53 +02:00
|
|
|
static int new_conflicts(const struct ref *refs)
|
|
|
|
{
|
|
|
|
const struct ref *ref;
|
|
|
|
const struct pkg *pkg;
|
|
|
|
|
|
|
|
for (ref = refs; ref; ref = ref->next)
|
|
|
|
for (pkg = ref->pkg->jrb->val; pkg; pkg = pkg->more)
|
|
|
|
if ((pkg->flags & (QPKG_INSTALLED | QPKG_ADDING)) ||
|
|
|
|
pkg->mark)
|
|
|
|
if (satisfies(pkg, ref))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-22 01:55:25 +02:00
|
|
|
/* ----- Recurse through lists and layers of dependencies ------------------ */
|
|
|
|
|
|
|
|
|
2010-11-22 01:48:24 +02:00
|
|
|
static void print_debug(const struct pkg *pkg, const struct stack *top,
|
|
|
|
int level)
|
|
|
|
{
|
|
|
|
const struct stack *p;
|
|
|
|
|
|
|
|
fprintf(stderr, "%*s", level, "");
|
|
|
|
fprintf(stderr, "%.*s %p", ID2PF(pkg->id), pkg);
|
|
|
|
if (pkg->version)
|
|
|
|
fprintf(stderr, " %.*s", ID2PF(pkg->version));
|
|
|
|
fprintf(stderr, " (");
|
|
|
|
for (p = top; p; p = p->next)
|
|
|
|
fprintf(stderr, "%s%.*s",
|
|
|
|
p == top ? "" : " ", ID2PF(p->pkg->id));
|
|
|
|
fprintf(stderr, ")");
|
|
|
|
if (pkg->mark)
|
|
|
|
fprintf(stderr, " +");
|
|
|
|
if (pkg->flags & QPKG_INSTALLED)
|
|
|
|
fprintf(stderr, " ***");
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-23 02:12:23 +02:00
|
|
|
static void resolve(struct list *next_deps, const struct ref *dep,
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
struct stack *top, struct list *conf)
|
2010-11-19 19:00:15 +02:00
|
|
|
{
|
2010-11-21 15:26:17 +02:00
|
|
|
static int level = 0;
|
2010-11-19 19:00:15 +02:00
|
|
|
struct list more_deps;
|
|
|
|
struct list more_conf = {
|
|
|
|
.next = conf
|
|
|
|
};
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
struct stack stack;
|
2010-11-19 19:00:15 +02:00
|
|
|
struct pkg *pkg;
|
|
|
|
|
|
|
|
while (!dep) {
|
2010-11-23 02:12:23 +02:00
|
|
|
if (!next_deps) {
|
2010-11-19 19:00:15 +02:00
|
|
|
done();
|
|
|
|
return;
|
|
|
|
}
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
assert(top->pkg->flags & QPKG_ADDING);
|
|
|
|
top->pkg->flags &= ~QPKG_ADDING;
|
|
|
|
top = top->next;
|
2010-11-23 02:12:23 +02:00
|
|
|
dep = next_deps->refs;
|
|
|
|
next_deps = next_deps->next;
|
2010-11-19 19:00:15 +02:00
|
|
|
}
|
2010-11-20 03:02:42 +02:00
|
|
|
for (pkg = dep->pkg->jrb->val; pkg; pkg = pkg->more) {
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
if (best && n_install == n_best)
|
2010-11-23 02:12:23 +02:00
|
|
|
break;
|
|
|
|
if (pkg->flags & QPKG_PROVIDED)
|
|
|
|
continue;
|
2010-11-22 01:48:24 +02:00
|
|
|
if (debug)
|
|
|
|
print_debug(pkg, top, level);
|
2010-11-19 20:26:24 +02:00
|
|
|
if (!satisfies(pkg, dep))
|
|
|
|
continue;
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
if (pkg->flags & QPKG_ADDING) {
|
|
|
|
fprintf(stderr, "package %.*s version %.*s "
|
|
|
|
"has cyclic dependency\n",
|
|
|
|
ID2PF(pkg->id), ID2PF(pkg->version));
|
|
|
|
exit(1);
|
|
|
|
}
|
2010-11-20 17:25:45 +02:00
|
|
|
if ((pkg->flags & QPKG_INSTALLED) || pkg->mark) {
|
2010-11-22 11:52:53 +02:00
|
|
|
assert(!old_conflicts(pkg, conf));
|
2010-11-23 02:12:23 +02:00
|
|
|
resolve(next_deps, dep->next, top, conf);
|
2010-11-19 19:00:15 +02:00
|
|
|
continue;
|
|
|
|
}
|
2010-11-22 11:52:53 +02:00
|
|
|
if (old_conflicts(pkg, conf))
|
|
|
|
continue;
|
|
|
|
if (new_conflicts(pkg->conflicts))
|
2010-11-19 19:00:15 +02:00
|
|
|
continue;
|
2010-11-21 15:26:17 +02:00
|
|
|
level++;
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
append_install(pkg);
|
qpkg: change prerequisite resolution order to be fully depth-first
Until now, when considering adding a package, we processed its
dependencies after completing the current list of dependencies. E.g.,
if we had a package A that depended on B and C, B depended on D, and C
depended on E, then the sequence would have been A, B, C, ...
We now process the dependencies of a package immediately after
considering the package, so the sequence above would become A, B, D,
C, ...
The advantage of the new order is that it becomes easier to follow the
dependency tree, which will be beneficial for loop detection and for
ordering packages by installation order.
- prereq.c (resolve): change prerequisite resolution order such that the
dependencies of the package being considered are processed immediately,
instead of deferring them until the end of the current dependency list
- prereq.c (prereq): we can now pass the list of dependencies directly,
without needing a list of lists element
- test/resorder: test resolution order
2010-11-21 20:38:46 +02:00
|
|
|
more_deps.refs = dep->next;
|
2010-11-23 02:12:23 +02:00
|
|
|
more_deps.next = next_deps;
|
2010-11-19 19:00:15 +02:00
|
|
|
more_conf.refs = pkg->conflicts;
|
|
|
|
more_conf.next = conf;
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
stack.pkg = pkg;
|
|
|
|
stack.next = top;
|
|
|
|
pkg->flags |= QPKG_ADDING;
|
|
|
|
resolve(&more_deps, pkg->depends, &stack, &more_conf);
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
backtrack();
|
2010-11-21 15:26:17 +02:00
|
|
|
level--;
|
2010-11-19 19:00:15 +02:00
|
|
|
}
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
|
|
|
|
/*
|
2010-11-23 02:12:23 +02:00
|
|
|
* @@@ this shouldn't be all of the story yet. If we fail a dependency
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
* due to a conflict, the current algorithm may leave packages marked
|
|
|
|
* as QPKG_ADDING, possibly triggering a false cyclic dependency error.
|
2010-11-23 02:12:23 +02:00
|
|
|
* See test/bug-adding for an example.
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
*
|
2010-11-23 02:12:23 +02:00
|
|
|
* In the case of cycle detection, we could avoid all the hassle of
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
* maintaining this flag and just walk down the stack to see if we're
|
|
|
|
* already trying to add the package (the stack should be correct with
|
|
|
|
* the current algorithm), but we'll need this kind of accurate
|
|
|
|
* tracking of package state for ordering the dependencies and for
|
|
|
|
* "Provides" anyway.
|
|
|
|
*/
|
2010-11-19 19:00:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-22 04:51:35 +02:00
|
|
|
static struct list *installed_conflicts(void)
|
|
|
|
{
|
|
|
|
struct list *list = NULL, *new;
|
|
|
|
const struct jrb *n;
|
|
|
|
const struct pkg *pkg;
|
|
|
|
|
|
|
|
for (n = jrb_first(packages->root); n != jrb_nil(packages->root);
|
|
|
|
n = jrb_next(n)) {
|
|
|
|
pkg = n->val;
|
|
|
|
if (!pkg)
|
|
|
|
continue;
|
|
|
|
if (!(pkg->flags & QPKG_INSTALLED))
|
|
|
|
continue;
|
|
|
|
if (!pkg->conflicts) /* optimization */
|
|
|
|
continue;
|
|
|
|
new = alloc_type(struct list);
|
|
|
|
new->refs = pkg->conflicts;
|
|
|
|
new->next = list;
|
|
|
|
list = new;
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void free_conflicts(struct list *conf)
|
|
|
|
{
|
|
|
|
struct list *next;
|
|
|
|
|
|
|
|
while (conf) {
|
|
|
|
next = conf->next;
|
|
|
|
free(conf);
|
|
|
|
conf = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-19 19:00:15 +02:00
|
|
|
struct pkg **prereq(struct pkg *pkg)
|
|
|
|
{
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
struct stack stack = {
|
|
|
|
.pkg = pkg,
|
|
|
|
.next = NULL
|
|
|
|
};
|
2010-11-22 04:24:44 +02:00
|
|
|
struct list conf = {
|
|
|
|
.refs = pkg->conflicts,
|
2010-11-22 04:51:35 +02:00
|
|
|
.next = installed_conflicts()
|
2010-11-22 04:24:44 +02:00
|
|
|
};
|
|
|
|
|
2010-11-22 11:52:53 +02:00
|
|
|
if (old_conflicts(pkg, &conf) || new_conflicts(pkg->conflicts)) {
|
2010-11-22 04:51:35 +02:00
|
|
|
fprintf(stderr, "%.*s conflicts with installed packages\n",
|
|
|
|
ID2PF(pkg->id));
|
|
|
|
exit(1);
|
|
|
|
}
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
pkg->flags |= QPKG_ADDING;
|
2010-11-22 04:24:44 +02:00
|
|
|
resolve(NULL, pkg->depends, &stack, &conf);
|
qpkg: added detection of cyclic dependencies
We define a cyclic dependency as the possibility (!) of the prerequisites
of a package X including a package that depends on X, and issue an error
if encountering such a situation.
Note that, if the dependencies of X can be resolved in a manner that does
not include the cyclic dependency, qpkg will still fail if it encounters
the cycle. Also note that qpkg (at least so far) does no perform an
exhaustive search to ferret out cyclic dependencies.
Furthermore, we don't consider that a cyclic dependency may not necessarily
imply a real life problem. E.g., if a package A contains elements X and
Y, with X needing package B, and the content of package B has a run-time
dependency on Y, the cyclic dependency between A and B would not exist
when considering its constituents. Since we don't have this information, we
just err on the side of caution.
- qpkg.h (enum flags): divide flags into categories (parse-time and
run-time) and add flag QPKG_ADDING to mark packets whose dependencies we
are processing
- prereq.c (resolve, prereq): track which packages we're tentatively
considering for installation and detect cyclic dependencies
- test/cyclic: regression test for detection of cyclic dependencies
- TODO: updated with recent changes
2010-11-22 01:31:20 +02:00
|
|
|
pkg->flags &= ~QPKG_ADDING;
|
qpkg: rearrange things in prereq.c to free the stack paradigm for other uses
- prereq.c (stack, epoch, push, pop, resolve, prereq): renamed to
"installs"
- prereq.c (n_stack, stack_max, push, pop, resolve, prereq): changed
"stack" to "install"
- prereq.c (push): renamed "push" to "append_install"
- prereq.c (pop): renamed "pop" to "backtrack"
2010-11-21 13:12:14 +02:00
|
|
|
free(installs);
|
2010-11-22 04:51:35 +02:00
|
|
|
free_conflicts(conf.next);
|
2010-11-19 19:00:15 +02:00
|
|
|
return best;
|
|
|
|
}
|