1
0
mirror of git://projects.qi-hardware.com/wernermisc.git synced 2024-11-15 08:19:41 +02:00
wernermisc/qpkg/prereq.c
Werner Almesberger 0229051f07 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 17:34:17 -03:00

231 lines
4.3 KiB
C

/*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "id.h"
#include "qpkg.h"
#include "util.h"
#include "prereq.h"
struct list {
const struct ref *refs;
struct list *next;
};
static struct pkg **best = NULL;
static struct pkg **installs = NULL;
static int n_best; /* undefined if best == NULL */
static int n_install = 0;
static int install_max = 0;
static int debug = 0;
static int epoch(const char **s, const struct id *id)
{
const char *end = id->s+id->len;
int n = 0;
while (*s != end && isdigit(**s)) {
n = 10*n+**s-'0';
(*s)++;
}
if (*s == id->s || (*s != end && **s == ':'))
return n;
*s = id->s;
return 0;
}
static int comp_versions(const struct id *va, const struct id *vb)
{
int epoch_a = 0, epoch_b = 0;
const char *a = va->s;
const char *b = vb->s;
const char *ea = a+va->len;
const char *eb = b+vb->len;
epoch_a = epoch(&a, va);
epoch_b = epoch(&b, vb);
if (epoch_a != epoch_b)
return epoch_a-epoch_b;
while (1) {
if (a == ea || b == eb)
return (a != ea)-(b != eb);
if (isdigit(*a) && isdigit(*b)) {
int na = 0, nb = 0;
while (a != ea && isdigit(*a)) {
na = na*10+*a-'0';
a++;
}
while (b != eb && isdigit(*b)) {
nb = nb*10+*b-'0';
b++;
}
if (na == nb)
continue;
return na-nb;
}
if (*a > *b)
return 1;
if (*a < *b)
return 1;
a++;
b++;
}
}
static void done(void)
{
int size;
if (best && n_best <= n_install)
return;
free(best);
size = sizeof(*installs)*(n_install+1);
best = alloc_size(size);
memcpy(best, installs, sizeof(*installs)*n_install);
n_best = n_install;
best[n_best] = NULL;
}
static void append_install(struct pkg *pkg)
{
if (n_install == install_max) {
install_max = (install_max+1)*2;
installs = realloc(installs, sizeof(*installs)*install_max);
if (!installs) {
perror("realloc");
exit(1);
}
}
installs[n_install++] = pkg;
assert(!pkg->mark && !(pkg->flags & QPKG_INSTALLED));
pkg->mark = 1;
}
static void backtrack(void)
{
assert(n_install);
n_install--;
installs[n_install]->mark = 0;
}
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;
assert(pkg->version);
cmp = comp_versions(pkg->version, ref->version);
//fprintf(stderr, "%.*s <%d> %.*s\n",
// ID2PF(pkg->version), cmp, ID2PF(ref->version));
switch (ref->relop) {
case rel_eq:
return !cmp;
case rel_ge:
return cmp >= 0;
case rel_lt:
return cmp < 0;
default:
abort();
}
}
static int conflicts(const struct pkg *pkg, const struct list *conf)
{
/* @@@ */
return 0;
}
static void resolve(struct list *next_dep, const struct ref *dep,
struct list *conf)
{
static int level = 0;
struct list more_deps;
struct list more_conf = {
.next = conf
};
struct pkg *pkg;
while (!dep) {
if (!next_dep) {
done();
return;
}
dep = next_dep->refs;
next_dep = next_dep->next;
}
for (pkg = dep->pkg->jrb->val; pkg; pkg = pkg->more) {
if (best && n_install == n_best)
return;
if (debug) {
fprintf(stderr, "%*s", level, "");
fprintf(stderr, "%.*s %p", ID2PF(pkg->id), pkg);
if (pkg->version)
fprintf(stderr, " %.*s", ID2PF(pkg->version));
if (pkg->mark)
fprintf(stderr, " +");
if (pkg->flags & QPKG_INSTALLED)
fprintf(stderr, " ***");
fprintf(stderr, "\n");
}
if (!satisfies(pkg, dep))
continue;
if ((pkg->flags & QPKG_INSTALLED) || pkg->mark) {
assert(!conflicts(pkg, conf));
resolve(next_dep, dep->next, conf);
continue;
}
if (conflicts(pkg, conf))
continue;
level++;
append_install(pkg);
more_deps.refs = dep->next;
more_deps.next = next_dep;
more_conf.refs = pkg->conflicts;
more_conf.next = conf;
resolve(&more_deps, pkg->depends, &more_conf);
backtrack();
level--;
}
}
struct pkg **prereq(struct pkg *pkg)
{
/* @@@ make list of pre-existing conflicts */
resolve(NULL, pkg->depends, NULL);
free(installs);
return best;
}