1
0
mirror of git://projects.qi-hardware.com/wernermisc.git synced 2024-11-29 09:42:48 +02:00
wernermisc/qpkg/prereq.c
Werner Almesberger 0ca4751b77 qpkg: some cleanup, prerequisite search speedup
With the reduction of search depth, "prereq abiword" takes about 3 minutes
on my PC. (Obviously, this can still be improved.)

- gobble.c (gobble_buf): if pkg->id->value is NULL, just it as such
- prereq.c (push): abort if trying to consider a package already considered
  or installed
- prereq.c (conflicts): before being satisfied with using an installed
  package, make sure it really satisfies the requirement
- prereq.c (conflicts): abort if an installed package conflicts
- prereq.c (conflicts): added some debugging output (temporary)
- prereq.c (conflicts): cut the search if we can't do better than a
  previous match
2010-11-19 15:26:24 -03:00

231 lines
4.0 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "id.h"
#include "qpkg.h"
#include "util.h"
struct list {
const struct ref *refs;
struct list *next;
};
static struct pkg **best = NULL;
static struct pkg **stack = NULL;
static int n_best; /* undefined if best == NULL */
static int n_stack = 0;
static int stack_max = 0;
#define ID2S(id) (int) (id)->len, (id)->s
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_stack)
return;
free(best);
size = sizeof(*stack)*(n_stack+1);
best = alloc_size(size);
memcpy(best, stack, sizeof(*stack)*n_stack);
n_best = n_stack;
best[n_best] = NULL;
}
static void push(struct pkg *pkg)
{
//fprintf(stderr, "push %.*s\n", ID2S(pkg->id));
if (n_stack == stack_max) {
stack_max = (stack_max+1)*2;
stack = realloc(stack, sizeof(*stack)*stack_max);
if (!stack) {
perror("realloc");
exit(1);
}
}
stack[n_stack++] = pkg;
assert(!pkg->mark && !pkg->installed);
pkg->mark = 1;
}
static void pop(void)
{
assert(n_stack);
n_stack--;
stack[n_stack]->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",
// ID2S(pkg->version), cmp, ID2S(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->value; pkg; pkg = pkg->more) {
if (best && n_stack == n_best)
return;
#if 0
fprintf(stderr, "%*s", level, "");
fprintf(stderr, "%.*s %p", ID2S(pkg->id), pkg);
if (pkg->version)
fprintf(stderr, " %.*s", ID2S(pkg->version));
if (pkg->mark) fprintf(stderr, " +");
if (pkg->installed) fprintf(stderr, " ***");
fprintf(stderr, "\n");
#endif
if (!satisfies(pkg, dep))
continue;
if (pkg->installed || pkg->mark) {
assert(!conflicts(pkg, conf));
resolve(next_dep, dep->next, conf);
continue;
}
if (conflicts(pkg, conf))
continue;
level++;
push(pkg);
more_deps.refs = pkg->depends;
more_deps.next = next_dep;
more_conf.refs = pkg->conflicts;
more_conf.next = conf;
resolve(&more_deps, dep->next, &more_conf);
next_dep = more_deps.next;
pop();
level--;
}
}
struct pkg **prereq(struct pkg *pkg)
{
struct list deps = {
.refs = pkg->depends,
.next = NULL
};
#if 0
/* make sure we don't return NULL if all dependencies are met */
if (!stack) {
stack = alloc_type(struct pkg *);
stack_max = 1;
}
#endif
/* @@@ make list of pre-existing conflicts */
resolve(&deps, NULL, NULL);
free(stack);
return best;
}