commit.c 18.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/* commit.c - Alpine Package Keeper (APK)
 * Apply solver calculated changes to database.
 *
 * Copyright (C) 2008-2013 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.
 */

12
#include <limits.h>
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
#include <stdint.h>
#include <unistd.h>
#include "apk_defines.h"
#include "apk_database.h"
#include "apk_package.h"
#include "apk_solver.h"

#include "apk_print.h"

static inline int pkg_available(struct apk_database *db, struct apk_package *pkg)
{
	if (pkg->repos & db->available_repos)
		return TRUE;
	return FALSE;
}

static int print_change(struct apk_database *db, struct apk_change *change,
			int cur, int total)
{
	struct apk_name *name;
	struct apk_package *oldpkg = change->old_pkg;
	struct apk_package *newpkg = change->new_pkg;
	const char *msg = NULL;
36
	char status[32];
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
	apk_blob_t *oneversion = NULL;
	int r;

	snprintf(status, sizeof(status), "(%i/%i)", cur+1, total);

	name = newpkg ? newpkg->name : oldpkg->name;
	if (oldpkg == NULL) {
		msg = "Installing";
		oneversion = newpkg->version;
	} else if (newpkg == NULL) {
		msg = "Purging";
		oneversion = oldpkg->version;
	} else if (newpkg == oldpkg) {
		if (change->reinstall) {
			if (pkg_available(db, newpkg))
52
				msg = "Reinstalling";
53
			else
54
				msg = "[APK unavailable, skipped] Reinstalling";
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
		} else if (change->old_repository_tag != change->new_repository_tag) {
			msg = "Updating pinning";
		}
		oneversion = newpkg->version;
	} else {
		r = apk_pkg_version_compare(newpkg, oldpkg);
		switch (r) {
		case APK_VERSION_LESS:
			msg = "Downgrading";
			break;
		case APK_VERSION_EQUAL:
			msg = "Replacing";
			break;
		case APK_VERSION_GREATER:
			msg = "Upgrading";
			break;
		}
	}
	if (msg == NULL)
		return FALSE;

	if (oneversion) {
77 78 79 80
		apk_message("%s %s %s" BLOB_FMT " (" BLOB_FMT ")",
			    status, msg,
			    name->name,
			    BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
81 82
			    BLOB_PRINTF(*oneversion));
	} else {
83 84 85 86
		apk_message("%s %s %s" BLOB_FMT " (" BLOB_FMT " -> " BLOB_FMT ")",
			    status, msg,
			    name->name,
			    BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
87 88 89 90 91 92 93 94
			    BLOB_PRINTF(*oldpkg->version),
			    BLOB_PRINTF(*newpkg->version));
	}
	return TRUE;
}

struct apk_stats {
	unsigned int changes;
95
	size_t bytes;
96 97 98 99 100
	unsigned int packages;
};

static void count_change(struct apk_change *change, struct apk_stats *stats)
{
101
	if (change->new_pkg != change->old_pkg || change->reinstall) {
102 103
		if (change->new_pkg != NULL) {
			stats->bytes += change->new_pkg->installed_size;
104
			stats->packages++;
105 106
		}
		if (change->old_pkg != NULL)
107
			stats->packages++;
108
		stats->changes++;
109 110
	} else if (change->new_repository_tag != change->old_repository_tag) {
		stats->packages++;
111 112 113 114 115 116 117 118 119 120
		stats->changes++;
	}
}

struct progress {
	struct apk_stats done;
	struct apk_stats total;
	struct apk_package *pkg;
};

121
static void progress_cb(void *ctx, size_t installed_bytes)
122 123
{
	struct progress *prog = (struct progress *) ctx;
124 125
	apk_print_progress(prog->done.bytes + prog->done.packages + installed_bytes,
			   prog->total.bytes + prog->total.packages);
126 127 128 129 130 131 132 133 134
}

static int dump_packages(struct apk_changeset *changeset,
			 int (*cmp)(struct apk_change *change),
			 const char *msg)
{
	struct apk_change *change;
	struct apk_name *name;
	struct apk_indent indent = { .indent = 2 };
135
	int match = 0;
136

137
	foreach_array_item(change, changeset->changes) {
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
		if (!cmp(change))
			continue;
		if (match == 0)
			printf("%s:\n", msg);
		if (change->new_pkg != NULL)
			name = change->new_pkg->name;
		else
			name = change->old_pkg->name;

		apk_print_indented(&indent, APK_BLOB_STR(name->name));
		match++;
	}
	if (match)
		printf("\n");
	return match;
}

static int cmp_remove(struct apk_change *change)
{
	return change->new_pkg == NULL;
}

static int cmp_new(struct apk_change *change)
{
	return change->old_pkg == NULL;
}

165 166 167 168 169
static int cmp_reinstall(struct apk_change *change)
{
	return change->reinstall;
}

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
static int cmp_downgrade(struct apk_change *change)
{
	if (change->new_pkg == NULL || change->old_pkg == NULL)
		return 0;
	if (apk_pkg_version_compare(change->new_pkg, change->old_pkg)
	    & APK_VERSION_LESS)
		return 1;
	return 0;
}

static int cmp_upgrade(struct apk_change *change)
{
	if (change->new_pkg == NULL || change->old_pkg == NULL)
		return 0;

	/* Count swapping package as upgrade too - this can happen if
	 * same package version is used after it was rebuilt against
	 * newer libraries. Basically, different (and probably newer)
	 * package, but equal version number. */
	if ((apk_pkg_version_compare(change->new_pkg, change->old_pkg) &
	     (APK_VERSION_GREATER | APK_VERSION_EQUAL)) &&
	    (change->new_pkg != change->old_pkg))
		return 1;

	return 0;
}

static void run_triggers(struct apk_database *db, struct apk_changeset *changeset)
{
199 200
	struct apk_change *change;
	struct apk_installed_package *ipkg;
201 202 203 204

	if (apk_db_fire_triggers(db) == 0)
		return;

205 206
	foreach_array_item(change, changeset->changes) {
		struct apk_package *pkg = change->new_pkg;
207 208 209
		if (pkg == NULL)
			continue;
		ipkg = pkg->ipkg;
Natanael Copa's avatar
Natanael Copa committed
210
		if (ipkg == NULL || ipkg->pending_triggers->num == 0)
211 212 213 214 215 216 217 218 219
			continue;

		*apk_string_array_add(&ipkg->pending_triggers) = NULL;
		apk_ipkg_run_script(ipkg, db, APK_SCRIPT_TRIGGER,
				    ipkg->pending_triggers->item);
		apk_string_array_free(&ipkg->pending_triggers);
	}
}

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
#define PRE_COMMIT_HOOK		0
#define POST_COMMIT_HOOK	1

struct apk_commit_hook {
	struct apk_database *db;
	int type;
};

static int run_commit_hook(void *ctx, int dirfd, const char *file)
{
	static char *const commit_hook_str[] = { "pre-commit", "post-commit" };
	struct apk_commit_hook *hook = (struct apk_commit_hook *) ctx;
	struct apk_database *db = hook->db;
	char fn[PATH_MAX], *argv[] = { fn, (char *) commit_hook_str[hook->type], NULL };

	if ((apk_flags & (APK_NO_SCRIPTS | APK_SIMULATE)) != 0)
		return 0;

	snprintf(fn, sizeof(fn), "etc/apk/commit_hooks.d" "/%s", file);
239 240 241 242
	if ((apk_flags & APK_NO_COMMIT_HOOKS) != 0) {
		apk_message("Skipping: %s %s", fn, commit_hook_str[hook->type]);
		return 0;
	}
243
	if (apk_verbosity >= 2) apk_message("Executing: %s %s", fn, commit_hook_str[hook->type]);
244 245

	if (apk_db_run_script(db, fn, argv) < 0 && hook->type == PRE_COMMIT_HOOK)
246
		return -2;
247 248 249 250 251 252 253 254 255 256 257

	return 0;
}

static int run_commit_hooks(struct apk_database *db, int type)
{
	struct apk_commit_hook hook = { .db = db, .type = type };
	return apk_dir_foreach_file(openat(db->root_fd, "etc/apk/commit_hooks.d", O_RDONLY | O_CLOEXEC),
				    run_commit_hook, &hook);
}

258 259 260 261 262 263
int apk_solver_commit_changeset(struct apk_database *db,
				struct apk_changeset *changeset,
				struct apk_dependency_array *world)
{
	struct progress prog;
	struct apk_change *change;
264 265 266
	char buf[32], size_unit;
	ssize_t size_diff = 0;
	int r, errors = 0;
267 268

	if (apk_db_check_world(db, world) != 0) {
269 270
		apk_error("Not committing changes due to missing repository tags. "
			  "Use --force-broken-world to override.");
271 272 273 274 275 276 277 278
		return -1;
	}

	if (changeset->changes == NULL)
		goto all_done;

	/* Count what needs to be done */
	memset(&prog, 0, sizeof(prog));
279
	foreach_array_item(change, changeset->changes) {
280 281
		count_change(change, &prog.total);
		if (change->new_pkg)
282
			size_diff += change->new_pkg->installed_size / 1024;
283
		if (change->old_pkg)
284
			size_diff -= change->old_pkg->installed_size / 1024;
285 286
	}
	size_unit = 'K';
287
	if (labs(size_diff) > 10000) {
288 289 290 291
		size_diff /= 1024;
		size_unit = 'M';
	}

292 293
	if ((apk_verbosity > 1 || (apk_flags & APK_INTERACTIVE)) &&
	    !(apk_flags & APK_SIMULATE)) {
294 295 296 297 298
		r = dump_packages(changeset, cmp_remove,
				  "The following packages will be REMOVED");
		r += dump_packages(changeset, cmp_downgrade,
				   "The following packages will be DOWNGRADED");
		if (r || (apk_flags & APK_INTERACTIVE) || apk_verbosity > 2) {
299 300 301 302 303 304
			r += dump_packages(changeset, cmp_new,
					   "The following NEW packages will be installed");
			r += dump_packages(changeset, cmp_upgrade,
					   "The following packages will be upgraded");
			r += dump_packages(changeset, cmp_reinstall,
					   "The following packages will be reinstalled");
305 306 307
			printf("After this operation, %zd %ciB of %s.\n",
				(size_diff < 0) ? -size_diff : size_diff,
				size_unit,
308
				(size_diff < 0) ?
309 310
				"disk space will be freed" :
				"additional disk space will be used");
311
		}
312
		if (r > 0 && (apk_flags & APK_INTERACTIVE)) {
313 314 315 316 317 318 319 320
			printf("Do you want to continue [Y/n]? ");
			fflush(stdout);
			r = fgetc(stdin);
			if (r != 'y' && r != 'Y' && r != '\n')
				return -1;
		}
	}

321
	if (run_commit_hooks(db, PRE_COMMIT_HOOK) == -2)
322 323
		return -1;

324
	/* Go through changes */
325
	foreach_array_item(change, changeset->changes) {
326 327 328
		r = change->old_pkg &&
			(change->old_pkg->ipkg->broken_files ||
			 change->old_pkg->ipkg->broken_script);
329 330 331 332
		if (print_change(db, change, prog.done.changes, prog.total.changes)) {
			prog.pkg = change->new_pkg;
			progress_cb(&prog, 0);

333 334 335 336 337
			if (!(apk_flags & APK_SIMULATE) &&
			    ((change->old_pkg != change->new_pkg) ||
			     (change->reinstall && pkg_available(db, change->new_pkg)))) {
				r = apk_db_install_pkg(db, change->old_pkg, change->new_pkg,
						       progress_cb, &prog) != 0;
338
			}
339 340
			if (r == 0 && change->new_pkg && change->new_pkg->ipkg)
				change->new_pkg->ipkg->repository_tag = change->new_repository_tag;
341
		}
342
		errors += r;
343 344
		count_change(change, &prog.done);
	}
345 346
	apk_print_progress(prog.total.bytes + prog.total.packages,
			   prog.total.bytes + prog.total.packages);
347

348
	apk_db_update_directory_permissions(db);
349 350 351 352 353
	run_triggers(db, changeset);

all_done:
	apk_dependency_array_copy(&db->world, world);
	apk_db_write_config(db);
354
	run_commit_hooks(db, POST_COMMIT_HOOK);
355

356
	if (!db->performing_self_upgrade) {
357
		if (errors)
358 359
			snprintf(buf, sizeof(buf), "%d error%s;", errors,
				 errors > 1 ? "s" : "");
360 361
		else
			strcpy(buf, "OK:");
362
		if (apk_verbosity > 1) {
363 364
			apk_message("%s %d packages, %d dirs, %d files, %zu MiB",
				    buf,
365 366 367 368 369
				    db->installed.stats.packages,
				    db->installed.stats.dirs,
				    db->installed.stats.files,
				    db->installed.stats.bytes / (1024 * 1024));
		} else {
370 371
			apk_message("%s %zu MiB in %d packages",
				    buf,
372 373 374 375
				    db->installed.stats.bytes / (1024 * 1024),
				    db->installed.stats.packages);
		}
	}
376
	return errors;
377 378
}

379
enum {
380 381 382
	STATE_PRESENT		= 0x80000000,
	STATE_MISSING		= 0x40000000,
	STATE_COUNT_MASK	= 0x0000ffff,
383
};
384

385 386 387 388 389 390 391 392 393
struct print_state {
	struct apk_database *db;
	struct apk_dependency_array *world;
	struct apk_indent i;
	struct apk_name_array *missing;
	const char *label;
	int num_labels;
	int match;
};
394

395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
static void label_start(struct print_state *ps, const char *text)
{
	if (ps->label) {
		printf("  %s:\n", ps->label);
		ps->label = NULL;
		ps->i.x = ps->i.indent = 0;
		ps->num_labels++;
	}
	if (ps->i.x == 0) {
		ps->i.x = printf("    %s", text);
		ps->i.indent = ps->i.x + 1;
	}
}
static void label_end(struct print_state *ps)
{
	if (ps->i.x != 0) {
		printf("\n");
		ps->i.x = ps->i.indent = 0;
	}
414 415
}

416
static void print_pinning_errors(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
417
{
418
	struct apk_database *db = ps->db;
419 420 421 422 423
	int i;

	if (pkg->ipkg != NULL)
		return;

424 425 426
	if (!(pkg->repos & db->available_repos)) {
		label_start(ps, "masked in:");
		apk_print_indented_fmt(&ps->i, "--no-network");
427 428 429
	} else if (pkg->repos == BIT(APK_REPOSITORY_CACHED)) {
		label_start(ps, "masked in:");
		apk_print_indented_fmt(&ps->i, "cache");
430 431 432 433 434 435 436 437
	} else {
		if (pkg->repos & apk_db_get_pinning_mask_repos(db, APK_DEFAULT_PINNING_MASK | BIT(tag)))
			return;
		for (i = 0; i < db->num_repo_tags; i++) {
			if (pkg->repos & db->repo_tags[i].allowed_repos) {
				label_start(ps, "masked in:");
				apk_print_indented(&ps->i, db->repo_tags[i].tag);
			}
438
		}
439
	}
440
	label_end(ps);
441 442
}

443
static void print_conflicts(struct print_state *ps, struct apk_package *pkg)
444
{
445 446
	struct apk_provider *p;
	struct apk_dependency *d;
447
	char tmp[256];
448
	int once;
449

450
	foreach_array_item(p, pkg->name->providers) {
451
		if (p->pkg == pkg || !p->pkg->marked)
452
			continue;
453 454 455 456
		label_start(ps, "conflicts:");
		apk_print_indented_fmt(&ps->i, PKG_VER_FMT, PKG_VER_PRINTF(p->pkg));
	}
	foreach_array_item(d, pkg->provides) {
457
		once = 1;
458
		foreach_array_item(p, d->name->providers) {
459 460
			if (!p->pkg->marked)
				continue;
461 462
			if (d->version == &apk_null_blob &&
			    p->version == &apk_null_blob)
463
				continue;
464 465
			if (once && p->pkg == pkg &&
			    p->version == d->version) {
466 467 468
				once = 0;
				continue;
			}
469 470
			label_start(ps, "conflicts:");
			apk_print_indented_fmt(
471
				&ps->i, PKG_VER_FMT "[%s]",
472
				PKG_VER_PRINTF(p->pkg),
473
				apk_dep_snprintf(tmp, sizeof(tmp), d));
474 475 476 477
		}
	}
	label_end(ps);
}
478

479
static void print_dep(struct apk_package *pkg0, struct apk_dependency *d0, struct apk_package *pkg, void *ctx)
480 481
{
	struct print_state *ps = (struct print_state *) ctx;
482
	const char *label = (ps->match & APK_DEP_SATISFIES) ? "satisfies:" : "breaks:";
483
	char tmp[256];
484

485 486 487 488 489 490 491 492 493 494 495 496
	label_start(ps, label);
	if (pkg0 == NULL)
		apk_print_indented_fmt(&ps->i, "world[%s]", apk_dep_snprintf(tmp, sizeof(tmp), d0));
	else
		apk_print_indented_fmt(&ps->i, PKG_VER_FMT "[%s]",
				       PKG_VER_PRINTF(pkg0),
				       apk_dep_snprintf(tmp, sizeof(tmp), d0));
}

static void print_deps(struct print_state *ps, struct apk_package *pkg, int match)
{
	ps->match = match;
497 498 499
	match |= APK_FOREACH_MARKED | APK_FOREACH_DEP;
	apk_pkg_foreach_matching_dependency(NULL, ps->world, match|apk_foreach_genid(), pkg, print_dep, ps);
	apk_pkg_foreach_reverse_dependency(pkg, match|apk_foreach_genid(), print_dep, ps);
500
	label_end(ps);
501 502
}

503
static void analyze_package(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
504
{
505
	char pkgtext[256];
506

507 508
	snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
	ps->label = pkgtext;
509

510 511
	print_pinning_errors(ps, pkg, tag);
	print_conflicts(ps, pkg);
512
	print_deps(ps, pkg, APK_DEP_CONFLICTS);
513
	if (ps->label == NULL)
514
		print_deps(ps, pkg, APK_DEP_SATISFIES);
515 516 517 518 519 520 521 522
}

static void analyze_name(struct print_state *ps, struct apk_name *name)
{
	struct apk_name **pname0, *name0;
	struct apk_provider *p0;
	struct apk_dependency *d0;
	char tmp[256];
523
	int refs;
524 525 526 527 528 529

	if (name->providers->num) {
		snprintf(tmp, sizeof(tmp), "%s (virtual)", name->name);
		ps->label = tmp;

		label_start(ps, "provided by:");
530 531
		foreach_array_item(p0, name->providers)
			p0->pkg->name->state_int++;
532
		foreach_array_item(p0, name->providers) {
533 534 535 536 537 538 539 540 541 542 543
			name0 = p0->pkg->name;
			refs = (name0->state_int & STATE_COUNT_MASK);
			if (refs == name0->providers->num) {
				/* name only */
				apk_print_indented(&ps->i, APK_BLOB_STR(name0->name));
				name0->state_int &= ~STATE_COUNT_MASK;
			} else if (refs > 0) {
				/* individual package */
				apk_print_indented_fmt(&ps->i, PKG_VER_FMT, PKG_VER_PRINTF(p0->pkg));
				name0->state_int--;
			}
544 545 546 547 548 549
		}
		label_end(ps);
	} else {
		snprintf(tmp, sizeof(tmp), "%s (missing)", name->name);
		ps->label = tmp;
	}
550

551 552 553
	label_start(ps, "required by:");
	foreach_array_item(d0, ps->world) {
		if (d0->name != name || d0->conflict)
554
			continue;
555 556 557 558 559 560
		apk_print_indented_fmt(&ps->i, "world[%s]",
			apk_dep_snprintf(tmp, sizeof(tmp), d0));
	}
	foreach_array_item(pname0, name->rdepends) {
		name0 = *pname0;
		foreach_array_item(p0, name0->providers) {
561
			if (!p0->pkg->marked)
562 563 564 565 566 567 568 569 570 571 572 573
				continue;
			foreach_array_item(d0, p0->pkg->depends) {
				if (d0->name != name || d0->conflict)
					continue;
				apk_print_indented_fmt(&ps->i,
					PKG_VER_FMT "[%s]",
					PKG_VER_PRINTF(p0->pkg),
					apk_dep_snprintf(tmp, sizeof(tmp), d0));
				break;
			}
			if (d0 != NULL)
				break;
574
		}
575 576 577
	}
	label_end(ps);
}
578

579 580 581
static void analyze_deps(struct print_state *ps, struct apk_dependency_array *deps)
{
	struct apk_dependency *d0;
582
	struct apk_name *name0;
583 584

	foreach_array_item(d0, deps) {
585
		name0 = d0->name;
586 587
		if (d0->conflict)
			continue;
588
		if ((name0->state_int & (STATE_PRESENT | STATE_MISSING)) != 0)
589
			continue;
590 591
		name0->state_int |= STATE_MISSING;
		analyze_name(ps, name0);
592 593 594 595 596 597 598
	}
}

void apk_solver_print_errors(struct apk_database *db,
			     struct apk_changeset *changeset,
			     struct apk_dependency_array *world)
{
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
	struct print_state ps;
	struct apk_change *change;
	struct apk_dependency *p;

	/* ERROR: unsatisfiable dependencies:
	 *   name:
	 *     required by: a b c d e
	 *     not available in any repository
	 *   name (virtual):
	 *     required by: a b c d e
	 *     provided by: foo bar zed
	 *   pkg-1.2:
	 *     masked by: @testing
	 *     satisfies: a[pkg]
	 *     conflicts: pkg-2.0 foo-1.2 bar-1.2
	 *     breaks: b[pkg>2] c[foo>2] d[!pkg]
	 *
	 * When two packages provide same name 'foo':
	 *   a-1:
	 *     satisfies: world[a]
	 *     conflicts: b-1[foo]
	 *   b-1:
	 *     satisfies: world[b]
	 *     conflicts: a-1[foo]
	 * 
	 *   c-1:
	 *     satisfies: world[a]
	 *     conflicts: c-1[foo]  (self-conflict by providing foo twice)
	 *
	 * When two packages get pulled in:
	 *   a-1:
	 *     satisfies: app1[so:a.so.1]
	 *     conflicts: a-2
	 *   a-2:
	 *     satisfies: app2[so:a.so.2]
	 *     conflicts: a-1
	 *
	 * satisfies lists all dependencies that is not satisfiable by
	 * any other selected version. or all of them with -v.
638
	 */
639 640
 
	apk_error("unsatisfiable constraints:");
641

642 643 644
	/* Construct information about names */
	foreach_array_item(change, changeset->changes) {
		struct apk_package *pkg = change->new_pkg;
645 646
		if (pkg == NULL)
			continue;
647
		pkg->marked = 1;
648
		pkg->name->state_int |= STATE_PRESENT;
649
		foreach_array_item(p, pkg->provides)
650
			p->name->state_int |= STATE_PRESENT;
651 652
	}

653 654 655 656 657 658 659
	/* Analyze is package, and missing names referred to */
	ps = (struct print_state) {
		.db = db,
		.world = world,
	};
	analyze_deps(&ps, world);
	foreach_array_item(change, changeset->changes) {
660 661 662
		struct apk_package *pkg = change->new_pkg;
		if (pkg == NULL)
			continue;
663 664
		analyze_package(&ps, pkg, change->new_repository_tag);
		analyze_deps(&ps, pkg->depends);
665 666
	}

667 668
	if (ps.num_labels == 0)
		printf("  Huh? Error reporter did not find the broken constraints.\n");
669 670 671 672 673 674 675 676 677 678
}

int apk_solver_commit(struct apk_database *db,
		      unsigned short solver_flags,
		      struct apk_dependency_array *world)
{
	struct apk_changeset changeset = {};
	int r;

	if (apk_db_check_world(db, world) != 0) {
679 680
		apk_error("Not committing changes due to missing repository tags. "
			  "Use --force-broken-world to override.");
681 682 683 684
		return -1;
	}

	r = apk_solver_solve(db, solver_flags, world, &changeset);
685
	if (r == 0)
686
		r = apk_solver_commit_changeset(db, &changeset, world);
687
	else
688 689
		apk_solver_print_errors(db, &changeset, world);

690
	apk_change_array_free(&changeset.changes);
691 692
	return r;
}