commit.c 21.1 KB
Newer Older
1
2
3
4
5
6
/* 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.
 *
7
 * SPDX-License-Identifier: GPL-2.0-only
8
9
 */

10
#include <assert.h>
11
#include <limits.h>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#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)
{
31
	struct apk_out *out = &db->ctx->out;
32
33
34
35
	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
81
		apk_msg(out, "%s %s %s" BLOB_FMT " (" BLOB_FMT ")",
			status, msg,
			name->name,
			BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
			BLOB_PRINTF(*oneversion));
82
	} else {
83
84
85
86
87
88
		apk_msg(out, "%s %s %s" BLOB_FMT " (" BLOB_FMT " -> " BLOB_FMT ")",
			status, msg,
			name->name,
			BLOB_PRINTF(db->repo_tags[change->new_repository_tag].tag),
			BLOB_PRINTF(*oldpkg->version),
			BLOB_PRINTF(*newpkg->version));
89
90
91
92
93
94
	}
	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
		stats->changes++;
	}
}

struct progress {
116
	struct apk_progress prog;
117
118
119
120
121
	struct apk_stats done;
	struct apk_stats total;
	struct apk_package *pkg;
};

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

130
static int dump_packages(struct apk_out *out, struct apk_changeset *changeset,
131
132
133
134
135
			 int (*cmp)(struct apk_change *change),
			 const char *msg)
{
	struct apk_change *change;
	struct apk_name *name;
136
	struct apk_indent indent;
137
	int match = 0;
138

139
	apk_print_indented_init(&indent, out, 0);
140
	foreach_array_item(change, changeset->changes) {
141
142
		if (!cmp(change)) continue;
		if (!match) apk_print_indented_group(&indent, 2, "%s:\n", msg);
143
144
145
146
147
148
149
150
		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++;
	}
151
	apk_print_indented_end(&indent);
152
153
154
155
156
157
158
159
160
161
162
163
164
	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
#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;
233
	struct apk_out *out = &db->ctx->out;
234
235
	char fn[PATH_MAX], *argv[] = { fn, (char *) commit_hook_str[hook->type], NULL };

236
	if (file[0] == '.') return 0;
237
	if ((db->ctx->flags & (APK_NO_SCRIPTS | APK_SIMULATE)) != 0) return 0;
238
239

	snprintf(fn, sizeof(fn), "etc/apk/commit_hooks.d" "/%s", file);
240
241
	if ((db->ctx->flags & APK_NO_COMMIT_HOOKS) != 0) {
		apk_msg(out, "Skipping: %s %s", fn, commit_hook_str[hook->type]);
Henrik Riomar's avatar
Henrik Riomar committed
242
243
		return 0;
	}
244
	apk_dbg(out, "Executing: %s %s", fn, commit_hook_str[hook->type]);
245
246

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

	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);
}

259
260
261
262
int apk_solver_commit_changeset(struct apk_database *db,
				struct apk_changeset *changeset,
				struct apk_dependency_array *world)
{
263
264
	struct apk_out *out = &db->ctx->out;
	struct progress prog = { .prog = db->ctx->progress };
265
	struct apk_change *change;
266
267
	char buf[32];
	const char *size_unit;
268
	off_t humanized, size_diff = 0, download_size = 0;
269
	int r, errors = 0;
270

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

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

	/* Count what needs to be done */
282
	foreach_array_item(change, changeset->changes) {
283
		count_change(change, &prog.total);
284
		if (change->new_pkg) {
285
			size_diff += change->new_pkg->installed_size;
286
287
288
289
			if (change->new_pkg != change->old_pkg &&
			    !(change->new_pkg->repos & db->local_repos))
				download_size += change->new_pkg->size;
		}
290
		if (change->old_pkg)
291
			size_diff -= change->old_pkg->installed_size;
292
293
	}

294
295
296
	if ((apk_out_verbosity(out) > 1 || (db->ctx->flags & APK_INTERACTIVE)) &&
	    !(db->ctx->flags & APK_SIMULATE)) {
		r = dump_packages(out, changeset, cmp_remove,
297
				  "The following packages will be REMOVED");
298
		r += dump_packages(out, changeset, cmp_downgrade,
299
				   "The following packages will be DOWNGRADED");
300
301
		if (r || (db->ctx->flags & APK_INTERACTIVE) || apk_out_verbosity(out) > 2) {
			r += dump_packages(out, changeset, cmp_new,
302
					   "The following NEW packages will be installed");
303
			r += dump_packages(out, changeset, cmp_upgrade,
304
					   "The following packages will be upgraded");
305
			r += dump_packages(out, changeset, cmp_reinstall,
306
					   "The following packages will be reinstalled");
307
308
			if (download_size) {
				size_unit = apk_get_human_size(download_size, &humanized);
309
				apk_msg(out, "Need to download %lld %s of packages.",
310
311
312
					(long long)humanized, size_unit);
			}
			size_unit = apk_get_human_size(llabs(size_diff), &humanized);
313
			apk_msg(out, "After this operation, %lld %s of %s.",
314
				(long long)humanized,
Timo Teräs's avatar
Timo Teräs committed
315
				size_unit,
316
				(size_diff < 0) ?
Timo Teräs's avatar
Timo Teräs committed
317
318
				"disk space will be freed" :
				"additional disk space will be used");
319
		}
320
		if (r > 0 && (db->ctx->flags & APK_INTERACTIVE)) {
321
322
323
			printf("Do you want to continue [Y/n]? ");
			fflush(stdout);
			r = fgetc(stdin);
324
			if (r != 'y' && r != 'Y' && r != '\n' && r != EOF)
325
326
327
328
				return -1;
		}
	}

329
	if (run_commit_hooks(db, PRE_COMMIT_HOOK) == -2)
330
331
		return -1;

332
	/* Go through changes */
333
	foreach_array_item(change, changeset->changes) {
334
335
336
		r = change->old_pkg &&
			(change->old_pkg->ipkg->broken_files ||
			 change->old_pkg->ipkg->broken_script);
337
338
339
340
		if (print_change(db, change, prog.done.changes, prog.total.changes)) {
			prog.pkg = change->new_pkg;
			progress_cb(&prog, 0);

341
			if (!(db->ctx->flags & APK_SIMULATE) &&
342
343
344
345
			    ((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;
346
			}
347
348
			if (r == 0 && change->new_pkg && change->new_pkg->ipkg)
				change->new_pkg->ipkg->repository_tag = change->new_repository_tag;
349
		}
350
		errors += r;
351
352
		count_change(change, &prog.done);
	}
353
	apk_print_progress(&prog.prog, prog.total.bytes + prog.total.packages,
354
			   prog.total.bytes + prog.total.packages);
355

356
	apk_db_update_directory_permissions(db);
357
358
359
360
	run_triggers(db, changeset);

all_done:
	apk_dependency_array_copy(&db->world, world);
361
	if (apk_db_write_config(db) != 0) errors++;
362
	run_commit_hooks(db, POST_COMMIT_HOOK);
363

364
	if (!db->performing_self_upgrade) {
365
		if (errors)
366
367
			snprintf(buf, sizeof(buf), "%d error%s;", errors,
				 errors > 1 ? "s" : "");
368
369
		else
			strcpy(buf, "OK:");
370
371
372
373
374
375
376
		if (apk_out_verbosity(out) > 1) {
			apk_msg(out, "%s %d packages, %d dirs, %d files, %zu MiB",
				buf,
				db->installed.stats.packages,
				db->installed.stats.dirs,
				db->installed.stats.files,
				db->installed.stats.bytes / (1024 * 1024));
377
		} else {
378
379
380
381
			apk_msg(out, "%s %zu MiB in %d packages",
				buf,
				db->installed.stats.bytes / (1024 * 1024),
				db->installed.stats.packages);
382
383
		}
	}
384
	return errors;
385
386
}

387
enum {
388
389
390
391
	STATE_PRESENT		= 0x80000000,
	STATE_MISSING		= 0x40000000,
	STATE_VIRTUAL_ONLY	= 0x20000000,
	STATE_INSTALLIF		= 0x10000000,
392
	STATE_COUNT_MASK	= 0x0000ffff,
393
};
394

395
396
397
398
399
400
401
402
403
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;
};
404

405
406
407
static void label_start(struct print_state *ps, const char *text)
{
	if (ps->label) {
408
		apk_print_indented_line(&ps->i, "  %s:\n", ps->label);
409
410
411
		ps->label = NULL;
		ps->num_labels++;
	}
412
	if (!ps->i.x) apk_print_indented_group(&ps->i, 0, "    %s", text);
413
414
415
}
static void label_end(struct print_state *ps)
{
416
	apk_print_indented_end(&ps->i);
417
418
}

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

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

427
428
429
	if (!(pkg->repos & db->available_repos)) {
		label_start(ps, "masked in:");
		apk_print_indented_fmt(&ps->i, "--no-network");
430
431
432
	} else if (!(BIT(pkg->layer) & db->active_layers)) {
		label_start(ps, "masked in:");
		apk_print_indented_fmt(&ps->i, "layer");
433
	} else if (pkg->repos == BIT(APK_REPOSITORY_CACHED) && !pkg->filename) {
434
435
		label_start(ps, "masked in:");
		apk_print_indented_fmt(&ps->i, "cache");
436
437
438
439
440
441
442
443
	} 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);
			}
444
		}
445
	}
446
	label_end(ps);
447
448
}

449
static void print_conflicts(struct print_state *ps, struct apk_package *pkg)
450
{
451
452
	struct apk_provider *p;
	struct apk_dependency *d;
453
	char tmp[256];
454
	int once;
455

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

485
static void print_dep(struct apk_package *pkg0, struct apk_dependency *d0, struct apk_package *pkg, void *ctx)
486
487
{
	struct print_state *ps = (struct print_state *) ctx;
488
	const char *label = (ps->match & APK_DEP_SATISFIES) ? "satisfies:" : "breaks:";
489
	char tmp[256];
490

491
492
493
494
495
496
497
498
499
500
501
502
	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;
503
504
505
	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);
506
	label_end(ps);
507
508
}

509
static void analyze_package(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
510
{
511
	char pkgtext[256];
512

513
514
	snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
	ps->label = pkgtext;
515

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

523
static void analyze_missing_name(struct print_state *ps, struct apk_name *name)
524
525
526
527
528
{
	struct apk_name **pname0, *name0;
	struct apk_provider *p0;
	struct apk_dependency *d0;
	char tmp[256];
529
	unsigned int genid;
530
	int refs;
531
532
533
534
535

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

536
537
538
539
		label_start(ps, "note:");
		apk_print_indented_words(&ps->i, "please select one of the 'provided by' packages explicitly");
		label_end(ps);

540
		label_start(ps, "provided by:");
541
542
		foreach_array_item(p0, name->providers)
			p0->pkg->name->state_int++;
543
		foreach_array_item(p0, name->providers) {
544
545
546
547
548
549
550
551
552
553
554
			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--;
			}
555
556
557
		}
		label_end(ps);
	} else {
558
		snprintf(tmp, sizeof(tmp), "%s (no such package)", name->name);
559
560
		ps->label = tmp;
	}
561

562
563
564
	label_start(ps, "required by:");
	foreach_array_item(d0, ps->world) {
		if (d0->name != name || d0->conflict)
565
			continue;
566
567
568
		apk_print_indented_fmt(&ps->i, "world[%s]",
			apk_dep_snprintf(tmp, sizeof(tmp), d0));
	}
569
	genid = apk_foreach_genid();
570
571
572
	foreach_array_item(pname0, name->rdepends) {
		name0 = *pname0;
		foreach_array_item(p0, name0->providers) {
573
			if (!p0->pkg->marked)
574
				continue;
575
576
577
			if (p0->pkg->foreach_genid == genid)
				continue;
			p0->pkg->foreach_genid = genid;
578
579
580
581
582
583
584
585
586
587
588
			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;
589
		}
590
591
592
	}
	label_end(ps);
}
593

594
595
596
static void analyze_deps(struct print_state *ps, struct apk_dependency_array *deps)
{
	struct apk_dependency *d0;
597
	struct apk_name *name0;
598
599

	foreach_array_item(d0, deps) {
600
		name0 = d0->name;
601
		if (d0->conflict) continue;
602
		if ((name0->state_int & (STATE_PRESENT | STATE_MISSING)) != 0)
603
			continue;
604
		name0->state_int |= STATE_MISSING;
605
606
607
608
609
		analyze_missing_name(ps, name0);
	}
}

static void discover_deps(struct apk_dependency_array *deps);
610
static void discover_name(struct apk_name *name, int pkg_state);
611

612
static void discover_reverse_iif(struct apk_name *name)
613
{
614
615
	struct apk_name **pname0, *name0;
	struct apk_dependency *d;
616
	struct apk_provider *p;
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641

	foreach_array_item(pname0, name->rinstall_if) {
		name0 = *pname0;

		foreach_array_item(p, name0->providers) {
			int ok = 1;
			if (!p->pkg->marked) continue;
			if (p->pkg->install_if->num == 0) continue;
			foreach_array_item(d, p->pkg->install_if) {
				if (!!d->conflict == !!(d->name->state_int & STATE_PRESENT)) {
					ok = 0;
					break;
				}
			}
			if (ok) {
				discover_name(p->pkg->name, STATE_INSTALLIF);
				foreach_array_item(d, p->pkg->provides)
					discover_name(d->name, STATE_INSTALLIF);
			}
		}
	}
}

static int is_name_concrete(struct apk_package *pkg, struct apk_name *name)
{
642
	struct apk_dependency *d;
643
644
645
646
647
648
649
650
	if (pkg->name == name) return 1;
	foreach_array_item(d, pkg->provides) {
		if (d->name != name) continue;
		if (d->version == &apk_atom_null) continue;
		return 1;
	}
	return 0;
}
651

652
653
654
655
static void discover_name(struct apk_name *name, int pkg_state)
{
	struct apk_provider *p;
	struct apk_dependency *d;
656
657

	foreach_array_item(p, name->providers) {
658
		int state = pkg_state;
659
		if (!p->pkg->marked) continue;
660
661
		if (state == STATE_PRESENT && !p->pkg->provider_priority &&
		    !is_name_concrete(p->pkg, name))
662
663
664
			state = STATE_VIRTUAL_ONLY;
		if (p->pkg->state_int & state) continue;
		p->pkg->state_int |= state;
665

666
667
668
669
670
671
672
		p->pkg->name->state_int |= state;
		foreach_array_item(d, p->pkg->provides)
			d->name->state_int |= state;

		discover_deps(p->pkg->depends);
		if (state == STATE_PRESENT || state == STATE_INSTALLIF) {
			discover_reverse_iif(p->pkg->name);
673
			foreach_array_item(d, p->pkg->provides)
674
				discover_reverse_iif(d->name);
675
676
677
678
679
680
681
682
683
684
		}
	}
}

static void discover_deps(struct apk_dependency_array *deps)
{
	struct apk_dependency *d;

	foreach_array_item(d, deps) {
		if (d->conflict) continue;
685
		discover_name(d->name, STATE_PRESENT);
686
687
688
689
690
691
692
	}
}

void apk_solver_print_errors(struct apk_database *db,
			     struct apk_changeset *changeset,
			     struct apk_dependency_array *world)
{
693
	struct apk_out *out = &db->ctx->out;
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
	struct print_state ps;
	struct apk_change *change;

	/* 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.
732
	 */
733
734
735
736
 
	/* Construct information about names */
	foreach_array_item(change, changeset->changes) {
		struct apk_package *pkg = change->new_pkg;
737
		if (pkg) pkg->marked = 1;
738
	}
739
	discover_deps(world);
740

741
742
743
744
745
	/* Analyze is package, and missing names referred to */
	ps = (struct print_state) {
		.db = db,
		.world = world,
	};
746
747
	apk_err(out, "unable to select packages:");
	apk_print_indented_init(&ps.i, out, 1);
748
749
	analyze_deps(&ps, world);
	foreach_array_item(change, changeset->changes) {
750
		struct apk_package *pkg = change->new_pkg;
751
		if (!pkg) continue;
752
753
		analyze_package(&ps, pkg, change->new_repository_tag);
		analyze_deps(&ps, pkg->depends);
754
755
	}

756
757
	if (!ps.num_labels)
		apk_print_indented_line(&ps.i, "Huh? Error reporter did not find the broken constraints.\n");
758
759
760
761
762
763
}

int apk_solver_commit(struct apk_database *db,
		      unsigned short solver_flags,
		      struct apk_dependency_array *world)
{
764
	struct apk_out *out = &db->ctx->out;
765
766
767
768
	struct apk_changeset changeset = {};
	int r;

	if (apk_db_check_world(db, world) != 0) {
769
770
		apk_err(out, "Not committing changes due to missing repository tags. "
			"Use --force-broken-world to override.");
771
772
773
774
		return -1;
	}

	r = apk_solver_solve(db, solver_flags, world, &changeset);
775
	if (r == 0)
776
		r = apk_solver_commit_changeset(db, &changeset, world);
777
	else
778
779
		apk_solver_print_errors(db, &changeset, world);

780
	apk_change_array_free(&changeset.changes);
781
782
	return r;
}