test.c 5.29 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* test.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2011 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

#include <errno.h>
#include <fcntl.h>
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_solver.h"
#include "apk_io.h"
#include "apk_print.h"

struct test_ctx {
	const char *installed_db;
	struct apk_string_array *repos;
Timo Teräs's avatar
Timo Teräs committed
22
	unsigned short solver_flags;
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
};

static int test_parse(void *pctx, struct apk_db_options *dbopts,
		      int optch, int optindex, const char *optarg)
{
	struct test_ctx *ctx = (struct test_ctx *) pctx;

	switch (optch) {
	case 0x10000:
		ctx->installed_db = optarg;
		break;
	case 0x10001:
		if (ctx->repos == NULL)
			apk_string_array_init(&ctx->repos);
		*apk_string_array_add(&ctx->repos) = (char*) optarg;
		break;
	case 'u':
Timo Teräs's avatar
Timo Teräs committed
40
		ctx->solver_flags |= APK_SOLVERF_UPGRADE;
41 42
		break;
	case 'a':
Timo Teräs's avatar
Timo Teräs committed
43
		ctx->solver_flags |= APK_SOLVERF_AVAILABLE;
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
		break;
	default:
		return -1;
	}
	return 0;
}

static inline void print_change(struct apk_package *oldpkg,
				struct apk_package *newpkg)
{
	const char *msg = NULL;
	struct apk_name *name;
	int r;

	if (oldpkg != NULL)
		name = oldpkg->name;
	else
		name = newpkg->name;

	if (oldpkg == NULL) {
		apk_message("Installing %s (" BLOB_FMT ")",
			    name->name,
			    BLOB_PRINTF(*newpkg->version));
	} else if (newpkg == NULL) {
		apk_message("Purging %s (" BLOB_FMT ")",
			    name->name,
			    BLOB_PRINTF(*oldpkg->version));
	} else {
		r = apk_pkg_version_compare(newpkg, oldpkg);
		switch (r) {
		case APK_VERSION_LESS:
			msg = "Downgrading";
			break;
		case APK_VERSION_EQUAL:
			if (newpkg == oldpkg)
				msg = "Re-installing";
			else
				msg = "Replacing";
			break;
		case APK_VERSION_GREATER:
			msg = "Upgrading";
			break;
		}
		apk_message("%s %s (" BLOB_FMT " -> " BLOB_FMT ")",
			    msg, name->name,
			    BLOB_PRINTF(*oldpkg->version),
			    BLOB_PRINTF(*newpkg->version));
	}
}

Timo Teräs's avatar
Timo Teräs committed
94
static void print_dep_errors(char *label, struct apk_dependency_array *deps)
95 96 97 98 99 100 101
{
	int i, print_label = 1;
	char buf[256];
	apk_blob_t p = APK_BLOB_BUF(buf);

	for (i = 0; i < deps->num; i++) {
		struct apk_dependency *dep = &deps->item[i];
Timo Teräs's avatar
Timo Teräs committed
102
		struct apk_package *pkg = dep->name->state_ptr;
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130

		if (pkg != NULL && apk_dep_is_satisfied(dep, pkg))
			continue;

		if (print_label) {
			print_label = 0;
			printf("%s: ", label);
		} else {
			printf(" ");
		}
		apk_blob_push_dep(&p, dep);
		p = apk_blob_pushed(APK_BLOB_BUF(buf), p);
		fwrite(p.ptr, p.len, 1, stdout);
	}
	if (!print_label)
		printf("\n");
}

static void print_errors_in_solution(struct apk_database *db, int unsatisfiable,
				     struct apk_package_array *solution)
{
	int i;

	printf("%d unsatisfiable dependencies (solution with %d names)\n",
		unsatisfiable, solution->num);

	for (i = 0; i < solution->num; i++) {
		struct apk_package *pkg = solution->item[i];
Timo Teräs's avatar
Timo Teräs committed
131
		pkg->name->state_ptr = pkg;
132 133
	}

Timo Teräs's avatar
Timo Teräs committed
134
	print_dep_errors("world", db->world);
135 136 137 138
	for (i = 0; i < solution->num; i++) {
		struct apk_package *pkg = solution->item[i];
		char pkgtext[256];
		snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(solution->item[i]));
Timo Teräs's avatar
Timo Teräs committed
139
		print_dep_errors(pkgtext, pkg->depends);
140 141 142 143
	}

}

144 145 146 147 148
static int test_main(void *pctx, struct apk_database *db, int argc, char **argv)
{
	struct test_ctx *ctx = (struct test_ctx *) pctx;
	struct apk_bstream *bs;
	struct apk_package_array *solution = NULL;
Timo Teräs's avatar
Timo Teräs committed
149
	struct apk_changeset changeset = {};
150
	int i, r;
151 152 153 154 155 156 157

	if (argc != 1)
		return -EINVAL;

	/* load installed db */
	if (ctx->installed_db != NULL) {
		bs = apk_bstream_from_file(AT_FDCWD, ctx->installed_db);
158 159 160 161
		if (bs != NULL) {
			apk_db_index_read(db, bs, -1);
			bs->close(bs, NULL);
		}
162 163 164 165 166 167
	}

	/* load additional indexes */
	if (ctx->repos) {
		for (i = 0; i < ctx->repos->num; i++) {
			bs = apk_bstream_from_file(AT_FDCWD, ctx->repos->item[i]);
168 169 170 171
			if (bs != NULL) {
				apk_db_index_read(db, bs, i);
				bs->close(bs, NULL);
			}
172 173 174 175 176 177 178
		}
	}

	/* construct new world */
	apk_deps_parse(db, &db->world, APK_BLOB_STR(argv[0]));

	/* run solver */
Timo Teräs's avatar
Timo Teräs committed
179
	r = apk_solver_solve(db, ctx->solver_flags, db->world, &solution, &changeset);
180
	if (r == 0) {
Timo Teräs's avatar
Timo Teräs committed
181 182 183 184
		/* dump changeset */
		for (i = 0; i < changeset.changes->num; i++) {
			struct apk_change *c = &changeset.changes->item[i];
			print_change(c->oldpkg, c->newpkg);
185
		}
186 187
	} else { /* r >= 1*/
		print_errors_in_solution(db, r, solution);
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
	}

	return 0;
}

static struct apk_option test_options[] = {
	{ 0x10000,	"installed",	"Installed database",
			required_argument, "DB" },
	{ 0x10001,	"raw-repository", "Add unsigned test repository index",
			required_argument, "INDEX" },
	{ 'u',		"upgrade",	"Prefer to upgrade package" },
	{ 'a',		"available",
	  "Re-install or downgrade if currently installed package is not "
	  "currently available from any repository" },
};

static struct apk_applet test_applet = {
	.name = "test",
	.help = "Test dependency graph solver (uses simple repository and installed db)",
	.arguments = "'WORLD'",
	.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_NO_REPOS,
	.context_size = sizeof(struct test_ctx),
	.num_options = ARRAY_SIZE(test_options),
	.options = test_options,
	.parse = test_parse,
	.main = test_main,
};

APK_DEFINE_APPLET(test_applet);