diff options
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | TODO_1.0 | 2 | ||||
-rw-r--r-- | VERSION.cmake | 2 | ||||
-rw-r--r-- | examples/solv/solv.c | 18 | ||||
-rw-r--r-- | ext/repo_deb.c | 166 | ||||
-rw-r--r-- | ext/repo_rpmdb.c | 51 | ||||
-rw-r--r-- | ext/testcase.c | 114 | ||||
-rw-r--r-- | ext/testcase.h | 1 | ||||
-rw-r--r-- | package/libsolv.changes | 9 | ||||
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/cleandeps.c | 1394 | ||||
-rw-r--r-- | src/libsolv.ver | 1 | ||||
-rw-r--r-- | src/order.c | 116 | ||||
-rw-r--r-- | src/policy.c | 6 | ||||
-rw-r--r-- | src/problems.c | 7 | ||||
-rw-r--r-- | src/rules.c | 1502 | ||||
-rw-r--r-- | src/solver.c | 418 | ||||
-rw-r--r-- | src/solver.h | 6 | ||||
-rw-r--r-- | src/solver_private.h | 25 | ||||
-rw-r--r-- | src/solver_util.c | 409 | ||||
-rw-r--r-- | test/testcases/cleandeps/cleandeps_dup.t | 18 | ||||
-rw-r--r-- | test/testcases/cleandeps/cleandeps_in.t | 3 | ||||
-rw-r--r-- | test/testcases/cleandeps/cleandeps_up.t | 23 | ||||
-rw-r--r-- | test/testcases/distupgrade/dup_multiversion3.t | 11 | ||||
-rw-r--r-- | test/testcases/distupgrade/dup_noarchchange.t | 5 | ||||
-rw-r--r-- | test/testcases/distupgrade/dup_orphan3.t | 23 | ||||
-rw-r--r-- | test/testcases/targeted/targeted_up.t | 2 | ||||
-rw-r--r-- | tools/testsolv.c | 47 |
29 files changed, 2438 insertions, 1955 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7902065..605ab76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,9 +156,9 @@ IF (${have_system} STRGREATER xx) ENDIF (${have_system} STRGREATER xx) SET (ENABLE_ZLIB_COMPRESSION ON) -IF (ENABLE_ARCHREPO) +IF (ENABLE_ARCHREPO OR ENABLE_DEBIAN) SET (ENABLE_LZMA_COMPRESSION ON) -ENDIF (ENABLE_ARCHREPO) +ENDIF (ENABLE_ARCHREPO OR ENABLE_DEBIAN) IF (ENABLE_RPMMD OR ENABLE_SUSEREPO OR ENABLE_APPDATA OR ENABLE_COMPS OR ENABLE_HELIXREPO OR ENABLE_MDKREPO) IF (WITH_LIBXML2 ) @@ -2,6 +2,14 @@ This file contains the major changes between libsolv versions: +Version 0.6.30 +- new features: + * many fixes and extenstions for cleandeps, e.g. + cleandeps now works for "update all packages" + * support debian packages with xz compressed control.tar + * always create dup rules for "distupgrade" jobs + * use recommends also for ordering packages + Version 0.6.29 - new features: * support for REL_WITHOUT and REL_UNLESS dependencies @@ -23,8 +23,6 @@ - write more manpages -- use dup rules all the time - - bindings: selections.flags() should be a attribute and not a method - rename repodata_lookup_id_uninternalized to repodata_lookup_id_voidid_uninternalized diff --git a/VERSION.cmake b/VERSION.cmake index f24ed63..b047bfc 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "0") SET(LIBSOLV_MAJOR "0") SET(LIBSOLV_MINOR "6") -SET(LIBSOLV_PATCH "29") +SET(LIBSOLV_PATCH "30") diff --git a/examples/solv/solv.c b/examples/solv/solv.c index 615c83d..5ee4297 100644 --- a/examples/solv/solv.c +++ b/examples/solv/solv.c @@ -36,6 +36,7 @@ #include "solver.h" #include "solverdebug.h" #include "transaction.h" +#include "testcase.h" #ifdef SUSE #include "repo_autopattern.h" #endif @@ -228,6 +229,7 @@ main(int argc, char **argv) int keyname_depstr = 0; int debuglevel = 0; int answer, acnt = 0; + char *testcase = 0; argc--; argv++; @@ -326,6 +328,12 @@ main(int argc, char **argv) argc -= 2; argv += 2; } + else if (argc > 2 && !strcmp(argv[1], "--testcase")) + { + testcase = argv[2]; + argc -= 2; + argv += 2; + } else break; } @@ -679,7 +687,15 @@ rerunsolver: Id problem, solution; int pcnt, scnt; - if (!solver_solve(solv, &job)) + pcnt = solver_solve(solv, &job); + if (testcase) + { + printf("Writing solver testcase:\n"); + if (!testcase_write(solv, testcase, TESTCASE_RESULT_TRANSACTION | TESTCASE_RESULT_PROBLEMS, 0, 0)) + printf("%s\n", pool_errstr(pool)); + testcase = 0; + } + if (!pcnt) break; pcnt = solver_problem_count(solv); printf("Found %d problems:\n", pcnt); diff --git a/ext/repo_deb.c b/ext/repo_deb.c index 812f3d9..277e65a 100644 --- a/ext/repo_deb.c +++ b/ext/repo_deb.c @@ -12,6 +12,7 @@ #include <string.h> #include <unistd.h> #include <zlib.h> +#include <lzma.h> #include <errno.h> #include "pool.h" @@ -22,11 +23,37 @@ #include "repo_deb.h" static unsigned char * -decompress(unsigned char *in, int inl, int *outlp) +decompress_gz(unsigned char *in, int inl, int *outlp, int maxoutl) { z_stream strm; int outl, ret; - unsigned char *out; + unsigned char *bp, *out; + + /* first skip the gz header */ + if (inl <= 10 || in[0] != 0x1f || in[1] != 0x8b) + return 0; + if (in[2] != 8 || (in[3] & 0xe0) != 0) + return 0; + bp = in + 4; + bp += 6; /* skip time, xflags and OS code */ + if (in[3] & 0x04) + { + /* skip extra field */ + int l = bp + 2 >= in + inl ? 0 : (bp[0] | bp[1] << 8); + bp += l + 2; + } + if (in[3] & 0x08) /* orig filename */ + while (bp < in + inl && *bp++) + ; + if (in[3] & 0x10) /* file comment */ + while (bp < in + inl && *bp++) + ; + if (in[3] & 0x02) /* header crc */ + bp += 2; + if (bp >= in + inl) + return 0; + inl -= bp - in; + in = bp; memset(&strm, 0, sizeof(strm)); strm.next_in = in; @@ -46,6 +73,12 @@ decompress(unsigned char *in, int inl, int *outlp) if (strm.avail_out == 0) { outl += 4096; + if (outl >= maxoutl) + { + inflateEnd(&strm); + free(out); + return 0; + } out = solv_realloc(out, outl + 4096); strm.next_out = out + outl; strm.avail_out = 4096; @@ -55,6 +88,7 @@ decompress(unsigned char *in, int inl, int *outlp) break; if (ret != Z_OK) { + inflateEnd(&strm); free(out); return 0; } @@ -65,6 +99,58 @@ decompress(unsigned char *in, int inl, int *outlp) return out; } +static unsigned char * +decompress_xz(unsigned char *in, int inl, int *outlp, int maxoutl) +{ + static lzma_stream stream_init = LZMA_STREAM_INIT; + lzma_stream strm; + int outl, ret; + unsigned char *out; + + strm = stream_init; + strm.next_in = in; + strm.avail_in = inl; + out = solv_malloc(4096); + strm.next_out = out; + strm.avail_out = 4096; + outl = 0; + ret = lzma_auto_decoder(&strm, 100 << 20, 0); + if (ret != LZMA_OK) + { + free(out); + return 0; + } + for (;;) + { + if (strm.avail_out == 0) + { + outl += 4096; + if (outl >= maxoutl) + { + lzma_end(&strm); + free(out); + return 0; + } + out = solv_realloc(out, outl + 4096); + strm.next_out = out + outl; + strm.avail_out = 4096; + } + ret = lzma_code(&strm, LZMA_RUN); + if (ret == LZMA_STREAM_END) + break; + if (ret != LZMA_OK) + { + lzma_end(&strm); + free(out); + return 0; + } + } + outl += 4096 - strm.avail_out; + lzma_end(&strm); + *outlp = outl; + return out; +} + static Id parseonedep(Pool *pool, char *p) { @@ -447,6 +533,10 @@ repo_add_debdb(Repo *repo, int flags) return 0; } +#define CONTROL_COMP_NONE 0 +#define CONTROL_COMP_GZIP 1 +#define CONTROL_COMP_XZ 2 + Id repo_add_deb(Repo *repo, const char *deb, int flags) { @@ -454,6 +544,7 @@ repo_add_deb(Repo *repo, const char *deb, int flags) Repodata *data; unsigned char buf[4096], *bp; int l, l2, vlen, clen, ctarlen; + int control_comp; unsigned char *ctgz; unsigned char pkgid[16]; unsigned char *ctar; @@ -495,16 +586,23 @@ repo_add_deb(Repo *repo, const char *deb, int flags) fclose(fp); return 0; } - if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz ", 16) != 0 && strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz/ ", 16) != 0) + control_comp = 0; + if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz/ ", 16)) + control_comp = CONTROL_COMP_GZIP; + else if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar.xz ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar.xz/ ", 16)) + control_comp = CONTROL_COMP_XZ; + else if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar/ ", 16)) + control_comp = CONTROL_COMP_NONE; + else { - pool_error(pool, -1, "%s: control.tar.gz is not second entry", deb); + pool_error(pool, -1, "%s: control.tar is not second entry", deb); fclose(fp); return 0; } clen = atoi((char *)buf + 8 + 60 + vlen + 48); if (clen <= 0 || clen >= 0x100000) { - pool_error(pool, -1, "%s: control.tar.gz has illegal size", deb); + pool_error(pool, -1, "%s: control.tar has illegal size", deb); fclose(fp); return 0; } @@ -534,55 +632,25 @@ repo_add_deb(Repo *repo, const char *deb, int flags) solv_chksum_free(chk, pkgid); gotpkgid = 1; } - if (ctgz[0] != 0x1f || ctgz[1] != 0x8b) - { - pool_error(pool, -1, "%s: control.tar.gz is not gzipped", deb); - solv_free(ctgz); - return 0; - } - if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0) - { - pool_error(pool, -1, "%s: control.tar.gz is compressed in a strange way", deb); - solv_free(ctgz); - return 0; - } - bp = ctgz + 4; - bp += 6; /* skip time, xflags and OS code */ - if (ctgz[3] & 0x04) - { - /* skip extra field */ - l = bp[0] | bp[1] << 8; - bp += l + 2; - if (bp >= ctgz + clen) - { - pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb); - solv_free(ctgz); - return 0; - } - } - if (ctgz[3] & 0x08) /* orig filename */ - while (*bp) - bp++; - if (ctgz[3] & 0x10) /* file comment */ - while (*bp) - bp++; - if (ctgz[3] & 0x02) /* header crc */ - bp += 2; - if (bp >= ctgz + clen) + ctar = 0; + if (control_comp == CONTROL_COMP_GZIP) + ctar = decompress_gz(ctgz, clen, &ctarlen, 0x1000000); + else if (control_comp == CONTROL_COMP_XZ) + ctar = decompress_xz(ctgz, clen, &ctarlen, 0x1000000); + else { - pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb); - solv_free(ctgz); - return 0; + ctarlen = clen; + ctar = solv_memdup(ctgz, clen); } - ctar = decompress(bp, ctgz + clen - bp, &ctarlen); solv_free(ctgz); if (!ctar) { - pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb); + pool_error(pool, -1, "%s: control.tar is corrupt", deb); return 0; } bp = ctar; l = ctarlen; + l2 = 0; while (l > 512) { int j; @@ -590,6 +658,12 @@ repo_add_deb(Repo *repo, const char *deb, int flags) for (j = 124; j < 124 + 12; j++) if (bp[j] >= '0' && bp[j] <= '7') l2 = l2 * 8 + (bp[j] - '0'); + if (l2 < 0 || l2 > l) + { + l2 = 0; + break; + } + bp[124] = 0; if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control")) break; l2 = 512 + ((l2 + 511) & ~511); @@ -598,7 +672,7 @@ repo_add_deb(Repo *repo, const char *deb, int flags) } if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0) { - pool_error(pool, -1, "%s: control.tar.gz contains no control file", deb); + pool_error(pool, -1, "%s: control.tar contains no control file", deb); free(ctar); return 0; } diff --git a/ext/repo_rpmdb.c b/ext/repo_rpmdb.c index 9fb5a29..ceaf351 100644 --- a/ext/repo_rpmdb.c +++ b/ext/repo_rpmdb.c @@ -167,11 +167,12 @@ #endif /* some limits to guard against corrupt rpms */ -#define MAX_SIG_CNT 0x100000 -#define MAX_SIG_DSIZE 0x100000 +/* dsize limits taken from rpm's lib/header.c */ +#define MAX_SIG_CNT 0x10000 +#define MAX_SIG_DSIZE 0x4000000 -#define MAX_HDR_CNT 0x100000 -#define MAX_HDR_DSIZE 0x2000000 +#define MAX_HDR_CNT 0x10000 +#define MAX_HDR_DSIZE 0x10000000 typedef struct rpmhead { int cnt; @@ -1190,14 +1191,15 @@ rpmdbid2db(unsigned char *db, Id id, int byteswapped) int serialize_dbenv_ops(struct rpmdbstate *state) { - char lpath[PATH_MAX]; + char *lpath; mode_t oldmask; int fd; struct flock fl; - snprintf(lpath, PATH_MAX, "%s/var/lib/rpm/.dbenv.lock", state->rootdir ? state->rootdir : ""); + lpath = solv_dupjoin(state->rootdir, "/var/lib/rpm/.dbenv.lock", 0); oldmask = umask(022); fd = open(lpath, (O_RDWR|O_CREAT), 0644); + free(lpath); umask(oldmask); if (fd < 0) return -1; @@ -1221,7 +1223,7 @@ static int opendbenv(struct rpmdbstate *state) { const char *rootdir = state->rootdir; - char dbpath[PATH_MAX]; + char *dbpath; DB_ENV *dbenv = 0; int r; @@ -1230,13 +1232,15 @@ opendbenv(struct rpmdbstate *state) #if (defined(FEDORA) || defined(MAGEIA)) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5)) dbenv->set_thread_count(dbenv, 8); #endif - snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm", rootdir ? rootdir : ""); + dbpath = solv_dupjoin(rootdir, "/var/lib/rpm", 0); if (access(dbpath, W_OK) == -1) { - snprintf(dbpath, PATH_MAX, "%s/usr/share/rpm/Packages", rootdir ? rootdir : ""); + free(dbpath); + dbpath = solv_dupjoin(rootdir, "/usr/share/rpm/Packages", 0); if (access(dbpath, R_OK) == 0) state->is_ostree = 1; - snprintf(dbpath, PATH_MAX, "%s%s", rootdir ? rootdir : "", state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm"); + free(dbpath); + dbpath = solv_dupjoin(rootdir, state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm", 0); r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0); } else @@ -1253,9 +1257,11 @@ opendbenv(struct rpmdbstate *state) if (r) { pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno)); + free(dbpath); dbenv->close(dbenv, 0); return 0; } + free(dbpath); state->dbenv = dbenv; return 1; } @@ -1286,6 +1292,22 @@ closedbenv(struct rpmdbstate *state) state->dbenv = 0; } +static int +stat_database(struct rpmdbstate *state, char *dbname, struct stat *statbuf, int seterror) +{ + char *dbpath; + dbpath = solv_dupjoin(state->rootdir, state->is_ostree ? "/usr/share/rpm/" : "/var/lib/rpm/", dbname); + if (stat(dbpath, statbuf)) + { + if (seterror) + pool_error(state->pool, -1, "%s: %s", dbpath, strerror(errno)); + free(dbpath); + return -1; + } + free(dbpath); + return 0; +} + #endif static void @@ -1534,7 +1556,6 @@ static int count_headers(struct rpmdbstate *state) { Pool *pool = state->pool; - char dbpath[PATH_MAX]; struct stat statbuf; DB *db = 0; DBC *dbc = 0; @@ -1542,8 +1563,7 @@ count_headers(struct rpmdbstate *state) DBT dbkey; DBT dbdata; - snprintf(dbpath, PATH_MAX, "%s%s/Name", state->rootdir ? state->rootdir : "", state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm"); - if (stat(dbpath, &statbuf)) + if (stat_database(state, "Name", &statbuf, 0)) return 0; memset(&dbkey, 0, sizeof(dbkey)); memset(&dbdata, 0, sizeof(dbdata)); @@ -1836,7 +1856,6 @@ int repo_add_rpmdb(Repo *repo, Repo *ref, int flags) { Pool *pool = repo->pool; - char dbpath[PATH_MAX]; struct stat packagesstat; unsigned char newcookie[32]; const unsigned char *oldcookie = 0; @@ -1870,10 +1889,8 @@ repo_add_rpmdb(Repo *repo, Repo *ref, int flags) } /* XXX: should get ro lock of Packages database! */ - snprintf(dbpath, PATH_MAX, "%s%s/Packages", state.rootdir ? state.rootdir : "", state.is_ostree ? "/usr/share/rpm" : "/var/lib/rpm"); - if (stat(dbpath, &packagesstat)) + if (stat_database(&state, "Packages", &packagesstat, 1)) { - pool_error(pool, -1, "%s: %s", dbpath, strerror(errno)); freestate(&state); return -1; } diff --git a/ext/testcase.c b/ext/testcase.c index 77cc719..1f19be1 100644 --- a/ext/testcase.c +++ b/ext/testcase.c @@ -85,6 +85,7 @@ static struct resultflags2str { { TESTCASE_RESULT_RULES, "rules" }, { TESTCASE_RESULT_GENID, "genid" }, { TESTCASE_RESULT_REASON, "reason" }, + { TESTCASE_RESULT_CLEANDEPS, "cleandeps" }, { 0, 0 } }; @@ -925,7 +926,7 @@ str2selflags(Pool *pool, char *s) /* modifies the string! */ break; } if (!selflags2str[i].str) - pool_debug(pool, SOLV_ERROR, "str2job: unknown selection flag '%s'\n", s); + pool_error(pool, 0, "str2job: unknown selection flag '%s'", s); s = se; } return selflags; @@ -947,7 +948,7 @@ str2jobflags(Pool *pool, char *s) /* modifies the string */ break; } if (!jobflags2str[i].str) - pool_debug(pool, SOLV_ERROR, "str2job: unknown job flag '%s'\n", s); + pool_error(pool, 0, "str2job: unknown job flag '%s'", s); s = se; } return jobflags; @@ -982,7 +983,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp) } if (npieces < 3) { - pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str); + pool_error(pool, -1, "str2job: bad line '%s'", str); solv_free(pieces); return -1; } @@ -992,7 +993,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp) break; if (!job2str[i].str) { - pool_debug(pool, SOLV_ERROR, "str2job: unknown job '%s'\n", str); + pool_error(pool, -1, "str2job: unknown job '%s'", str); solv_free(pieces); return -1; } @@ -1013,7 +1014,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp) { if (npieces != 3) { - pool_debug(pool, SOLV_ERROR, "str2job: bad pkg selector in '%s'\n", str); + pool_error(pool, -1, "str2job: bad pkg selector in '%s'", str); solv_free(pieces); return -1; } @@ -1021,7 +1022,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp) what = testcase_str2solvid(pool, pieces[2]); if (!what) { - pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[2]); + pool_error(pool, -1, "str2job: unknown package '%s'", pieces[2]); solv_free(pieces); return -1; } @@ -1069,7 +1070,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp) Id p = testcase_str2solvid(pool, pieces[i]); if (!p) { - pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[i]); + pool_error(pool, -1, "str2job: unknown package '%s'", pieces[i]); queue_free(&q); solv_free(pieces); return -1; @@ -1085,14 +1086,14 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp) Repo *repo; if (npieces != 3) { - pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str); + pool_error(pool, -1, "str2job: bad line '%s'", str); solv_free(pieces); return -1; } repo = testcase_str2repo(pool, pieces[2]); if (!repo) { - pool_debug(pool, SOLV_ERROR, "str2job: unknown repo '%s'\n", pieces[2]); + pool_error(pool, -1, "str2job: unknown repo '%s'", pieces[2]); solv_free(pieces); return -1; } @@ -1103,7 +1104,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp) { if (npieces != 3 && strcmp(pieces[2], "packages") != 0) { - pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str); + pool_error(pool, -1, "str2job: bad line '%s'", str); solv_free(pieces); return -1; } @@ -1112,7 +1113,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp) } else { - pool_debug(pool, SOLV_ERROR, "str2job: unknown selection in '%s'\n", str); + pool_error(pool, -1, "str2job: unknown selection in '%s'", str); solv_free(pieces); return -1; } @@ -1133,10 +1134,7 @@ addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue) if (!strcmp(pieces[0], job2str[i].str)) break; if (!job2str[i].str) - { - pool_debug(pool, SOLV_ERROR, "selstr2job: unknown job '%s'\n", pieces[0]); - return -1; - } + return pool_error(pool, -1, "selstr2job: unknown job '%s'", pieces[0]); job = job2str[i].job; if (npieces > 3) { @@ -1150,10 +1148,7 @@ addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue) } } if (npieces < 4) - { - pool_debug(pool, SOLV_ERROR, "selstr2job: no selection flags\n"); - return -1; - } + return pool_error(pool, -1, "selstr2job: no selection flags"); selflags = str2selflags(pool, pieces[3]); queue_init(&sel); r = selection_make(pool, &sel, pieces[2], selflags); @@ -1534,10 +1529,7 @@ testcase_setpoolflags(Pool *pool, const char *str) if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0) break; if (!poolflags2str[i].str) - { - pool_debug(pool, SOLV_ERROR, "setpoolflags: unknown flag '%.*s'\n", (int)(p - s), s); - return 0; - } + return pool_error(pool, 0, "setpoolflags: unknown flag '%.*s'", (int)(p - s), s); pool_set_flag(pool, poolflags2str[i].flag, v); } return 1; @@ -1591,15 +1583,9 @@ testcase_setsolverflags(Solver *solv, const char *str) if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0) break; if (!solverflags2str[i].str) - { - pool_debug(solv->pool, SOLV_ERROR, "setsolverflags: unknown flag '%.*s'\n", (int)(p - s), s); - return 0; - } + return pool_error(solv->pool, 0, "setsolverflags: unknown flag '%.*s'", (int)(p - s), s); if (solver_set_flag(solv, solverflags2str[i].flag, v) == -1) - { - pool_debug(solv->pool, SOLV_ERROR, "setsolverflags: unsupported flag '%s'\n", solverflags2str[i].str); - return 0; - } + return pool_error(solv->pool, 0, "setsolverflags: unsupported flag '%s'", solverflags2str[i].str); } return 1; } @@ -2121,6 +2107,19 @@ testcase_solverresult(Solver *solv, int resultflags) } queue_free(&whyq); } + if ((resultflags & TESTCASE_RESULT_CLEANDEPS) != 0) + { + Queue q; + + queue_init(&q); + solver_get_cleandeps(solv, &q); + for (i = 0; i < q.count; i++) + { + s = pool_tmpjoin(pool, "cleandeps ", testcase_solvid2str(pool, q.elements[i]), 0); + strqueue_push(&sq, s); + } + queue_free(&q); + } strqueue_sort(&sq); result = strqueue_join(&sq); strqueue_free(&sq); @@ -2147,10 +2146,7 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha resultname = "solver.result"; if (mkdir(dir, 0777) && errno != EEXIST) - { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not create directory '%s'\n", dir); - return 0; - } + return pool_error(solv->pool, 0, "testcase_write: could not create directory '%s'", dir); strqueue_init(&sq); FOR_REPOS(repoid, repo) { @@ -2171,14 +2167,14 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha out = pool_tmpjoin(pool, dir, "/", out); if (!(fp = solv_xfopen(out, "w"))) { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out); + pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out); strqueue_free(&sq); return 0; } testcase_write_testtags(repo, fp); if (fclose(fp)) { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n"); + pool_error(solv->pool, 0, "testcase_write: write error"); strqueue_free(&sq); return 0; } @@ -2303,14 +2299,14 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha out = pool_tmpjoin(pool, dir, "/", resultname); if (!(fp = fopen(out, "w"))) { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out); + pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out); solv_free(result); strqueue_free(&sq); return 0; } if (result && *result && fwrite(result, strlen(result), 1, fp) != 1) { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n"); + pool_error(solv->pool, 0, "testcase_write: write error"); solv_free(result); strqueue_free(&sq); fclose(fp); @@ -2318,7 +2314,7 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha } if (fclose(fp)) { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n"); + pool_error(solv->pool, 0, "testcase_write: write error"); strqueue_free(&sq); return 0; } @@ -2330,20 +2326,20 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha out = pool_tmpjoin(pool, dir, "/", testcasename); if (!(fp = fopen(out, "w"))) { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out); + pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out); strqueue_free(&sq); return 0; } if (*cmd && fwrite(cmd, strlen(cmd), 1, fp) != 1) { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n"); + pool_error(solv->pool, 0, "testcase_write: write error"); strqueue_free(&sq); fclose(fp); return 0; } if (fclose(fp)) { - pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n"); + pool_error(solv->pool, 0, "testcase_write: write error"); strqueue_free(&sq); return 0; } @@ -2497,7 +2493,7 @@ str2resultflags(Pool *pool, char *s) /* modifies the string! */ break; } if (!resultflags2str[i].str) - pool_debug(pool, SOLV_ERROR, "result: unknown flag '%s'\n", s); + pool_error(pool, 0, "result: unknown flag '%s'", s); s = se; } return resultflags; @@ -2527,7 +2523,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res *resultflagsp = 0; if (!fp && !(fp = fopen(testcase, "r"))) { - pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", testcase); + pool_error(pool, 0, "testcase_read: could not open '%s'", testcase); return 0; } testcasedir = solv_strdup(testcase); @@ -2617,7 +2613,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res } if (!rfp) { - pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata); + pool_error(pool, 0, "testcase_read: could not open '%s'", rdata); } else if (!strcmp(repotype, "testtags")) { @@ -2639,7 +2635,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res else { fclose(rfp); - pool_debug(pool, SOLV_ERROR, "testcase_read: unknown repo type for repo '%s'\n", repo->name); + pool_error(pool, 0, "testcase_read: unknown repo type for repo '%s'", repo->name); } } } @@ -2675,7 +2671,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res } if (!(dp && *dp)) { - pool_debug(pool, SOLV_ERROR, "testcase_read: system: could not change disttype to '%s'\n", pieces[2]); + pool_error(pool, 0, "testcase_read: system: could not change disttype to '%s'", pieces[2]); missing_features = 1; } } @@ -2689,7 +2685,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res { Repo *repo = testcase_str2repo(pool, pieces[3]); if (!repo) - pool_debug(pool, SOLV_ERROR, "testcase_read: system: unknown repo '%s'\n", pieces[3]); + pool_error(pool, 0, "testcase_read: system: unknown repo '%s'", pieces[3]); else pool_set_installed(pool, repo); } @@ -2727,7 +2723,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res s = strchr(pieces[1], '('); if (!s && pieces[1][i - 1] != ')') { - pool_debug(pool, SOLV_ERROR, "testcase_read: bad namespace '%s'\n", pieces[1]); + pool_error(pool, 0, "testcase_read: bad namespace '%s'", pieces[1]); } else { @@ -2790,7 +2786,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res { FILE *rfp = fopen(rdata, "r"); if (!rfp) - pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata); + pool_error(pool, 0, "testcase_read: could not open '%s'", rdata); else { result = read_file(rfp); @@ -2816,7 +2812,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res Id p; if (strcmp(pieces[1], "pkg")) { - pool_debug(pool, SOLV_ERROR, "testcase_read: bad disable type '%s'\n", pieces[1]); + pool_error(pool, 0, "testcase_read: bad disable type '%s'", pieces[1]); continue; } if (!prepared) @@ -2832,7 +2828,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res if (p) MAPCLR(pool->considered, p); else - pool_debug(pool, SOLV_ERROR, "disable: unknown package '%s'\n", pieces[2]); + pool_error(pool, 0, "disable: unknown package '%s'", pieces[2]); } else if (!strcmp(pieces[0], "feature")) { @@ -2844,7 +2840,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res break; if (!features[j]) { - pool_debug(pool, SOLV_ERROR, "testcase_read: missing feature '%s'\n", pieces[i]); + pool_error(pool, 0, "testcase_read: missing feature '%s'", pieces[i]); missing_features++; } } @@ -2871,12 +2867,12 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res break; if (!op->flags) { - pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown op '%s'\n", pieces[2]); + pool_error(pool, 0, "testcase_read: genid: unknown op '%s'", pieces[2]); break; } if (ngenid < 2) { - pool_debug(pool, SOLV_ERROR, "testcase_read: genid: out of stack\n"); + pool_error(pool, 0, "testcase_read: genid: out of stack"); break; } ngenid -= 2; @@ -2890,7 +2886,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res id = testcase_str2dep(pool, pieces[2]); else { - pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown command '%s'\n", pieces[1]); + pool_error(pool, 0, "testcase_read: genid: unknown command '%s'", pieces[1]); break; } genid[ngenid++] = id; @@ -2899,14 +2895,14 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res { if (strcmp(pieces[1], "name")) { - pool_debug(pool, SOLV_ERROR, "testcase_read: autoinst: illegal mode\n"); + pool_error(pool, 0, "testcase_read: autoinst: illegal mode"); break; } queue_push(&autoinstq, pool_str2id(pool, pieces[2], 1)); } else { - pool_debug(pool, SOLV_ERROR, "testcase_read: cannot parse command '%s'\n", pieces[0]); + pool_error(pool, 0, "testcase_read: cannot parse command '%s'", pieces[0]); } } while (job && ngenid > 0) diff --git a/ext/testcase.h b/ext/testcase.h index 341b9c2..2069637 100644 --- a/ext/testcase.h +++ b/ext/testcase.h @@ -18,6 +18,7 @@ #define TESTCASE_RESULT_RULES (1 << 6) #define TESTCASE_RESULT_GENID (1 << 7) #define TESTCASE_RESULT_REASON (1 << 8) +#define TESTCASE_RESULT_CLEANDEPS (1 << 9) /* reuse solver hack, testsolv use only */ #define TESTCASE_RESULT_REUSE_SOLVER (1 << 31) diff --git a/package/libsolv.changes b/package/libsolv.changes index 21bdbd1..17b1984 100644 --- a/package/libsolv.changes +++ b/package/libsolv.changes @@ -1,4 +1,13 @@ ------------------------------------------------------------------- +Mon Oct 23 11:40:22 CEST 2017 - mls@suse.de + +- many fixes and improvements for cleandeps +- support debian packages with xz compressed control.tar +- always create dup rules for "distupgrade" jobs +- use recommends also for ordering packages +- bump version to 0.6.30 + +------------------------------------------------------------------- Thu Sep 7 16:18:20 CEST 2017 - mls@suse.de - expose solver_get_recommendations in bindings diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76ea9f1..35516ba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,7 +19,7 @@ SET (libsolv_SRCS queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c chksum.c md5.c sha1.c sha2.c solvversion.c selection.c - fileprovides.c diskusage.c suse.c) + fileprovides.c diskusage.c suse.c solver_util.c cleandeps.c) SET (libsolv_HEADERS bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h diff --git a/src/cleandeps.c b/src/cleandeps.c new file mode 100644 index 0000000..cbe2020 --- /dev/null +++ b/src/cleandeps.c @@ -0,0 +1,1394 @@ +/* + * Copyright (c) 2017, SUSE Inc. + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +/* + * cleandeps.c + * + * code to find and erase unneeded packages + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "solver.h" +#include "solverdebug.h" +#include "solver_private.h" +#include "bitmap.h" +#include "pool.h" +#include "util.h" +#include "policy.h" +#include "cplxdeps.h" + +#undef CLEANDEPSDEBUG + +/* + * This functions collects all packages that are looked at + * when a dependency is checked. We need it to "pin" installed + * packages when removing a supplemented package in createcleandepsmap. + * Here's an not uncommon example: + * A contains "Supplements: packageand(B, C)" + * B contains "Requires: A" + * Now if we remove C, the supplements is no longer true, + * thus we also remove A. Without the dep_pkgcheck function, we + * would now also remove B, but this is wrong, as adding back + * C doesn't make the supplements true again. Thus we "pin" B + * when we remove A. + * There's probably a better way to do this, but I haven't come + * up with it yet ;) + */ +static void +dep_pkgcheck_slow(Solver *solv, Id dep, Map *m, Queue *q) +{ + Pool *pool = solv->pool; + Id p, pp; + + if (ISRELDEP(dep)) + { + Reldep *rd = GETRELDEP(pool, dep); + if (rd->flags >= 8) + { + if (rd->flags == REL_AND) + { + dep_pkgcheck_slow(solv, rd->name, m, q); + dep_pkgcheck_slow(solv, rd->evr, m, q); + return; + } + if (rd->flags == REL_COND || rd->flags == REL_UNLESS) + { + dep_pkgcheck_slow(solv, rd->name, m, q); + if (ISRELDEP(rd->evr)) + { + Reldep *rd2 = GETRELDEP(pool, rd->evr); + if (rd2->flags == REL_ELSE) + dep_pkgcheck_slow(solv, rd2->evr, m, q); + } + return; + } + if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES) + return; + } + } + FOR_PROVIDES(p, pp, dep) + if (!m || MAPTST(m, p)) + queue_push(q, p); +} + +static inline void +dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q) +{ + Pool *pool = solv->pool; + Id p, pp; + + if (!ISSIMPLEDEP(pool, dep)) + { + dep_pkgcheck_slow(solv, dep, m, q); + return; + } + FOR_PROVIDES(p, pp, dep) + if (!m || MAPTST(m, p)) + queue_push(q, p); +} + +static int +check_xsupp(Solver *solv, Queue *depq, Id dep) +{ + Pool *pool = solv->pool; + Id p, pp; + + if (ISRELDEP(dep)) + { + Reldep *rd = GETRELDEP(pool, dep); + if (rd->flags >= 8) + { + if (rd->flags == REL_AND) + { + if (!check_xsupp(solv, depq, rd->name)) + return 0; + return check_xsupp(solv, depq, rd->evr); + } + if (rd->flags == REL_OR) + { + if (check_xsupp(solv, depq, rd->name)) + return 1; + return check_xsupp(solv, depq, rd->evr); + } + if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES) + return 0; + if (depq && rd->flags == REL_NAMESPACE) + { + int i; + for (i = 0; i < depq->count; i++) + if (depq->elements[i] == dep || depq->elements[i] == rd->name) + return 1; + } + } + } + FOR_PROVIDES(p, pp, dep) + if (p == SYSTEMSOLVABLE || pool->solvables[p].repo == solv->installed) + return 1; + return 0; +} + +struct trj_data { + Queue *edges; + Id *low; + Id idx; + Id nstack; + Id firstidx; +}; + +/* Tarjan's SCC algorithm, slightly modifed */ +static void +trj_visit(struct trj_data *trj, Id node) +{ + Id *low = trj->low; + Queue *edges = trj->edges; + Id nnode, myidx, stackstart; + int i; + + low[node] = myidx = trj->idx++; + low[(stackstart = trj->nstack++)] = node; + for (i = edges->elements[node]; (nnode = edges->elements[i]) != 0; i++) + { + Id l = low[nnode]; + if (!l) + { + if (!edges->elements[edges->elements[nnode]]) + { + trj->idx++; + low[nnode] = -1; + continue; + } + trj_visit(trj, nnode); + l = low[nnode]; + } + if (l < 0) + continue; + if (l < trj->firstidx) + { + int k; + for (k = l; low[low[k]] == l; k++) + low[low[k]] = -1; + } + else if (l < low[node]) + low[node] = l; + } + if (low[node] == myidx) + { + if (myidx != trj->firstidx) + myidx = -1; + for (i = stackstart; i < trj->nstack; i++) + low[low[i]] = myidx; + trj->nstack = stackstart; + } +} + +#ifdef ENABLE_COMPLEX_DEPS +static void +complex_filter_unneeded(Pool *pool, Id ip, Id req, Queue *edges, Map *cleandepsmap, Queue *unneededq) +{ + int i, j; + Queue dq; + Id p; + + queue_init(&dq); + i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND); + if (i == 0 || i == 1) + { + queue_free(&dq); + return; + } + for (i = 0; i < dq.count; i++) + { + for (; (p = dq.elements[i]) != 0; i++) + { + if (p < 0) + { + if (pool->solvables[-p].repo != pool->installed) + break; + continue; + } + if (p == ip || pool->solvables[p].repo != pool->installed || !MAPTST(cleandepsmap, p - pool->installed->start)) + continue; + for (j = 0; j < unneededq->count; j++) + if (p == unneededq->elements[j]) + { + if (edges->elements[edges->count - 1] != j + 1) + queue_push(edges, j + 1); + break; + } + } + while (dq.elements[i]) + i++; + } + queue_free(&dq); +} +#endif + + +static void +filter_unneeded(Solver *solv, Queue *unneededq, Map *unneededmap, int justone) +{ + Pool *pool = solv->pool; + Repo *installed = solv->installed; + Queue edges; + Id *nrequires; + Map m, installedm; + int i, j, pass, count = unneededq->count; + Id *low; + + if (unneededq->count < 2) + return; + map_init(&m, 0); + if (!unneededmap) + { + unneededmap = &m; + map_grow(unneededmap, installed->end - installed->start); + for (i = 0; i < count; i++) + MAPSET(unneededmap, unneededq->elements[i] - installed->start); + } + map_init(&installedm, pool->nsolvables); + for (i = installed->start; i < installed->end; i++) + if (pool->solvables[i].repo == installed) + MAPSET(&installedm, i); + + nrequires = solv_calloc(count, sizeof(Id)); + queue_init(&edges); + queue_prealloc(&edges, count * 4 + 10); /* pre-size */ + + /* + * Go through the solvables in the nodes queue and create edges for + * all requires/recommends/supplements between the nodes. + * The edges are stored in the edges queue, we add 1 to the node + * index so that nodes in the edges queue are != 0 and we can + * terminate the edge list with 0. + * Thus for node element 5, the edges are stored starting at + * edges.elements[6] and are 0-terminated. + */ + /* leave first element zero to make things easier */ + /* also add trailing zero */ + queue_insertn(&edges, 0, 1 + count + 1, 0); + + /* first requires and recommends */ + for (i = 0; i < count; i++) + { + Solvable *s = pool->solvables + unneededq->elements[i]; + int oldcount = edges.count; + edges.elements[i + 1] = oldcount; + for (pass = 0; pass < 2; pass++) + { + unsigned int off = pass == 0 ? s->requires : s->recommends; + Id p, pp, dep, *dp; + if (off) + for (dp = s->repo->idarraydata + off; (dep = *dp) != 0; dp++) + { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, dep)) + { + complex_filter_unneeded(pool, s - pool->solvables, dep, &edges, unneededmap, unneededq); + continue; + } +#endif + if (justone) + { + int count = 0; + FOR_PROVIDES(p, pp, dep) + { + Solvable *sp = pool->solvables + p; + if (s == sp || sp->repo != installed) + continue; + count++; + } + if (count != 1) + continue; + } + FOR_PROVIDES(p, pp, dep) + { + Solvable *sp = pool->solvables + p; + if (s == sp || sp->repo != installed || !MAPTST(unneededmap, p - installed->start)) + continue; + for (j = 0; j < count; j++) + if (p == unneededq->elements[j]) + { + if (edges.elements[edges.count - 1] != j + 1) + queue_push(&edges, j + 1); + } + } + } + if (pass == 0) + nrequires[i] = edges.count - oldcount; + } + queue_push(&edges, 0); + } +#if 0 + printf("requires + recommends\n"); + for (i = 0; i < count; i++) + { + int j; + printf(" %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]); + for (j = edges.elements[i + 1]; edges.elements[j]; j++) + printf(" - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1])); + } +#endif + + /* then add supplements */ + for (i = 0; i < count; i++) + { + Solvable *s = pool->solvables + unneededq->elements[i]; + if (s->supplements) + { + Id *dp; + int k; + for (dp = s->repo->idarraydata + s->supplements; *dp; dp++) + if (solver_dep_possible(solv, *dp, &installedm)) + { + Queue iq; + Id iqbuf[16]; + queue_init_buffer(&iq, iqbuf, sizeof(iqbuf)/sizeof(*iqbuf)); + dep_pkgcheck(solv, *dp, 0, &iq); + if (justone && iq.count != 1) + { + queue_free(&iq); + continue; + } + for (k = 0; k < iq.count; k++) + { + Id p = iq.elements[k]; + Solvable *sp = pool->solvables + p; + if (p == unneededq->elements[i] || sp->repo != installed || !MAPTST(unneededmap, p - installed->start)) + continue; + for (j = 0; j < count; j++) + if (p == unneededq->elements[j]) + break; + /* now add edge from j + 1 to i + 1 */ + queue_insert(&edges, edges.elements[j + 1] + nrequires[j], i + 1); + /* addapt following edge pointers */ + for (j = j + 2; j < count + 1; j++) + edges.elements[j]++; + } + queue_free(&iq); + } + } + } +#if 0 + /* print result */ + printf("+ supplements\n"); + for (i = 0; i < count; i++) + { + int j; + printf(" %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]); + for (j = edges.elements[i + 1]; edges.elements[j]; j++) + printf(" - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1])); + } +#endif + map_free(&installedm); + + /* now run SCC algo two times, first with requires+recommends+supplements, + * then again without the requires. We run it the second time to get rid + * of packages that got dragged in via recommends/supplements */ + /* + * low will contain the result of the SCC search. + * it must be of at least size 2 * (count + 1) and + * must be zero initialized. + * The layout is: + * 0 low low ... low stack stack ...stack 0 + * count count + */ + low = solv_calloc(count + 1, 2 * sizeof(Id)); + for (pass = 0; pass < 2; pass++) + { + struct trj_data trj; + if (pass) + { + memset(low, 0, (count + 1) * (2 * sizeof(Id))); + for (i = 0; i < count; i++) + { + edges.elements[i + 1] += nrequires[i]; + if (!unneededq->elements[i]) + low[i + 1] = -1; /* ignore this node */ + } + } + trj.edges = &edges; + trj.low = low; + trj.idx = count + 1; /* stack starts here */ + for (i = 1; i <= count; i++) + { + if (low[i]) + continue; + if (edges.elements[edges.elements[i]]) + { + trj.firstidx = trj.nstack = trj.idx; + trj_visit(&trj, i); + } + else + { + Id myidx = trj.idx++; + low[i] = myidx; + low[myidx] = i; + } + } + /* prune packages */ + for (i = 0; i < count; i++) + if (low[i + 1] <= 0) + unneededq->elements[i] = 0; + } + solv_free(low); + solv_free(nrequires); + queue_free(&edges); + + /* finally remove all pruned entries from unneededq */ + for (i = j = 0; i < count; i++) + if (unneededq->elements[i]) + unneededq->elements[j++] = unneededq->elements[i]; + queue_truncate(unneededq, j); + map_free(&m); +} + + +#ifdef ENABLE_COMPLEX_DEPS +static void +complex_cleandeps_remove(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq) +{ + int i; + Queue dq; + Id p; + + queue_init(&dq); + i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND); + if (i == 0 || i == 1) + { + queue_free(&dq); + return; + } + for (i = 0; i < dq.count; i++) + { + for (; (p = dq.elements[i]) != 0; i++) + { + if (p < 0) + { + if (!MAPTST(installedm, -p)) + break; + continue; + } + if (p != SYSTEMSOLVABLE && MAPTST(im, p)) + { +#ifdef CLEANDEPSDEBUG + printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); +#endif + queue_push(iq, p); + } + } + while (dq.elements[i]) + i++; + } + queue_free(&dq); +} + +static void +complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq, Map *userinstalled) +{ + int i, blk; + Queue dq; + Id p; + + queue_init(&dq); + i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND); + if (i == 0 || i == 1) + { + queue_free(&dq); + return; + } + for (i = 0; i < dq.count; i++) + { + blk = i; + for (; (p = dq.elements[i]) != 0; i++) + { + if (p < 0) + { + if (!MAPTST(installedm, -p)) + break; + } + else if (p == ip) + break; + } + if (!p) + { + for (i = blk; (p = dq.elements[i]) != 0; i++) + { + if (p < 0) + continue; + if (MAPTST(im, p)) + continue; + if (!MAPTST(installedm, p)) + continue; + if (p == ip || MAPTST(userinstalled, p - pool->installed->start)) + continue; +#ifdef CLEANDEPSDEBUG + printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); +#endif + MAPSET(im, p); + queue_push(iq, p); + } + } + while (dq.elements[i]) + i++; + } + queue_free(&dq); +} + +#endif + +static inline int +queue_contains(Queue *q, Id id) +{ + int i; + for (i = 0; i < q->count; i++) + if (q->elements[i] == id) + return 1; + return 0; +} + +static void +find_update_seeds(Solver *solv, Queue *updatepkgs_filtered, Map *userinstalled) +{ + Pool *pool = solv->pool; + Repo *installed = solv->installed; + Queue *cleandeps_updatepkgs = solv->cleandeps_updatepkgs; + int i, j; + Id p; + + queue_prealloc(updatepkgs_filtered, cleandeps_updatepkgs->count); + for (i = 0; i < cleandeps_updatepkgs->count; i++) + { + p = cleandeps_updatepkgs->elements[i]; + if (pool->solvables[p].repo == installed) + { +#ifdef ENABLE_LINKED_PKGS + const char *name = pool_id2str(pool, pool->solvables[p].name); + if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0) + continue; +#endif + queue_push(updatepkgs_filtered, p); + } + } +#ifdef CLEANDEPSDEBUG + printf("SEEDS IN (%d)\n", updatepkgs_filtered->count); + for (i = 0; i < updatepkgs_filtered->count; i++) + printf(" - %s\n", pool_solvid2str(pool, updatepkgs_filtered->elements[i])); +#endif + filter_unneeded(solv, updatepkgs_filtered, 0, 1); +#ifdef CLEANDEPSDEBUG + printf("SEEDS OUT (%d)\n", updatepkgs_filtered->count); + for (i = 0; i < updatepkgs_filtered->count; i++) + printf(" - %s\n", pool_solvid2str(pool, updatepkgs_filtered->elements[i])); +#endif + /* make sure userinstalled packages are in the seeds */ + for (i = j = 0; i < updatepkgs_filtered->count; i++) + { + p = updatepkgs_filtered->elements[i]; + if (!MAPTST(userinstalled, p - installed->start)) + updatepkgs_filtered->elements[j++] = p; + } + queue_truncate(updatepkgs_filtered, j); + for (i = 0; i < cleandeps_updatepkgs->count; i++) + { + p = cleandeps_updatepkgs->elements[i]; + if (pool->solvables[p].repo == installed) + { +#ifdef ENABLE_LINKED_PKGS + const char *name = pool_id2str(pool, pool->solvables[p].name); + if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0) + { + queue_push(updatepkgs_filtered, p); + continue; + } +#endif + if (MAPTST(userinstalled, p - installed->start)) + queue_push(updatepkgs_filtered, p); + } + } +#ifdef CLEANDEPSDEBUG + printf("SEEDS FINAL\n"); + for (i = 0; i < updatepkgs_filtered->count; i++) + printf(" - %s\n", pool_solvid2str(pool, updatepkgs_filtered->elements[i])); +#endif +} + +/* + * Find all installed packages that are no longer + * needed regarding the current solver job. + * + * The algorithm is: + * - remove pass: remove all packages that could have + * been dragged in by the obsoleted packages. + * i.e. if package A is obsolete and contains "Requires: B", + * also remove B, as installing A will have pulled in B. + * after this pass, we have a set of still installed packages + * with broken dependencies. + * - add back pass: + * now add back all packages that the still installed packages + * require. + * + * The cleandeps packages are the packages removed in the first + * pass and not added back in the second pass. + * + * If we search for unneeded packages (unneeded is true), we + * simply remove all packages except the userinstalled ones in + * the first pass. + */ +void +solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded) +{ + Pool *pool = solv->pool; + Repo *installed = solv->installed; + Queue *job = &solv->job; + Map userinstalled; + Map im; + Map installedm; + Rule *r; + Id rid, how, what, select; + Id p, pp, ip, jp; + Id req, *reqp, sup, *supp; + Solvable *s; + Queue iq, iqcopy, xsuppq; + Queue updatepkgs_filtered; + int i; + + map_empty(cleandepsmap); + if (!installed || installed->end == installed->start) + return; + map_init(&userinstalled, installed->end - installed->start); + map_init(&im, pool->nsolvables); + map_init(&installedm, pool->nsolvables); + queue_init(&iq); + queue_init(&xsuppq); + + for (i = 0; i < job->count; i += 2) + { + how = job->elements[i]; + if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED) + { + what = job->elements[i + 1]; + select = how & SOLVER_SELECTMASK; + if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid)) + { + FOR_REPO_SOLVABLES(installed, p, s) + MAPSET(&userinstalled, p - installed->start); + } + FOR_JOB_SELECT(p, pp, select, what) + if (pool->solvables[p].repo == installed) + MAPSET(&userinstalled, p - installed->start); + } + if ((how & (SOLVER_JOBMASK | SOLVER_SELECTMASK)) == (SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES)) + { + what = job->elements[i + 1]; + if (ISRELDEP(what)) + { + Reldep *rd = GETRELDEP(pool, what); + if (rd->flags != REL_NAMESPACE) + continue; + if (rd->evr == 0) + { + queue_pushunique(&iq, rd->name); + continue; + } + FOR_PROVIDES(p, pp, what) + if (p) + break; + if (p) + continue; + queue_pushunique(&iq, what); + } + } + } + + /* have special namespace cleandeps erases */ + if (iq.count) + { + for (ip = installed->start; ip < installed->end; ip++) + { + s = pool->solvables + ip; + if (s->repo != installed) + continue; + if (!s->supplements) + continue; + supp = s->repo->idarraydata + s->supplements; + while ((sup = *supp++) != 0) + if (ISRELDEP(sup) && check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup)) + { +#ifdef CLEANDEPSDEBUG + printf("xsupp %s from %s\n", pool_dep2str(pool, sup), pool_solvid2str(pool, ip)); +#endif + queue_pushunique(&xsuppq, sup); + } + } + queue_empty(&iq); + } + + /* also add visible patterns to userinstalled for openSUSE */ + if (1) + { + Dataiterator di; + dataiterator_init(&di, pool, 0, 0, SOLVABLE_ISVISIBLE, 0, 0); + while (dataiterator_step(&di)) + { + Id *dp; + if (di.solvid <= 0) + continue; + s = pool->solvables + di.solvid; + if (!s->repo || !s->requires) + continue; + if (s->repo != installed && !pool_installable(pool, s)) + continue; + if (strncmp(pool_id2str(pool, s->name), "pattern:", 8) != 0) + continue; + dp = s->repo->idarraydata + s->requires; + for (dp = s->repo->idarraydata + s->requires; *dp; dp++) + FOR_PROVIDES(p, pp, *dp) + if (pool->solvables[p].repo == installed) + { + if (strncmp(pool_id2str(pool, pool->solvables[p].name), "pattern", 7) != 0) + continue; + MAPSET(&userinstalled, p - installed->start); + } + } + dataiterator_free(&di); + } + if (1) + { + /* all products and their buddies are userinstalled */ + for (p = installed->start; p < installed->end; p++) + { + Solvable *s = pool->solvables + p; + if (s->repo != installed) + continue; + if (!strncmp("product:", pool_id2str(pool, s->name), 8)) + { + MAPSET(&userinstalled, p - installed->start); +#ifdef ENABLE_LINKED_PKGS + if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1) + { + Id buddy = solv->instbuddy[p - installed->start]; + if (buddy >= installed->start && buddy < installed->end) + MAPSET(&userinstalled, buddy - installed->start); + } +#endif + } + } + } + + /* add all positive elements (e.g. locks) to "userinstalled" */ + for (rid = solv->jobrules; rid < solv->jobrules_end; rid++) + { + r = solv->rules + rid; + if (r->d < 0) + continue; + i = solv->ruletojob.elements[rid - solv->jobrules]; + if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS) + continue; + FOR_RULELITERALS(p, jp, r) + if (p > 0 && pool->solvables[p].repo == installed) + MAPSET(&userinstalled, p - installed->start); + } + + /* add all cleandeps candidates to iq */ + for (rid = solv->jobrules; rid < solv->jobrules_end; rid++) + { + r = solv->rules + rid; + if (r->d < 0) /* disabled? */ + continue; + if (r->d == 0 && r->p < 0 && r->w2 == 0) /* negative assertion (erase job)? */ + { + p = -r->p; + if (pool->solvables[p].repo != installed) + continue; + MAPCLR(&userinstalled, p - installed->start); + if (unneeded) + continue; + i = solv->ruletojob.elements[rid - solv->jobrules]; + how = job->elements[i]; + if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS)) + queue_push(&iq, p); + } + else if (r->p > 0) /* install job */ + { + if (unneeded) + continue; + i = solv->ruletojob.elements[rid - solv->jobrules]; + if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS) + { + /* check if the literals all obsolete some installed package */ + Map om; + int iqstart; + + /* just one installed literal */ + if (r->d == 0 && r->w2 == 0 && pool->solvables[r->p].repo == installed) + continue; + /* multiversion is bad */ + if (solv->multiversion.size && !solv->keepexplicitobsoletes) + { + FOR_RULELITERALS(p, jp, r) + if (MAPTST(&solv->multiversion, p)) + break; + if (p) + continue; + } + + om.size = 0; + iqstart = iq.count; + FOR_RULELITERALS(p, jp, r) + { + if (p < 0) + { + queue_truncate(&iq, iqstart); /* abort */ + break; + } + if (pool->solvables[p].repo == installed) + { + if (iq.count == iqstart) + queue_push(&iq, p); + else + { + for (i = iqstart; i < iq.count; i++) + if (iq.elements[i] == p) + break; + queue_truncate(&iq, iqstart); + if (i < iq.count) + queue_push(&iq, p); + } + } + else + solver_intersect_obsoleted(solv, p, &iq, iqstart, &om); + if (iq.count == iqstart) + break; + } + if (om.size) + map_free(&om); + } + } + } + queue_init_clone(&iqcopy, &iq); + + if (!unneeded) + { + if (solv->cleandeps_updatepkgs) + for (i = 0; i < solv->cleandeps_updatepkgs->count; i++) + queue_push(&iq, solv->cleandeps_updatepkgs->elements[i]); + } + + if (unneeded) + queue_empty(&iq); /* just in case... */ + + /* clear userinstalled bit for the packages we really want to delete/update */ + for (i = 0; i < iq.count; i++) + { + p = iq.elements[i]; + if (pool->solvables[p].repo != installed) + continue; + MAPCLR(&userinstalled, p - installed->start); + } + + for (p = installed->start; p < installed->end; p++) + { + if (pool->solvables[p].repo != installed) + continue; + MAPSET(&installedm, p); + if (pool->considered && !MAPTST(pool->considered, p)) + MAPSET(&userinstalled, p - installed->start); /* we may not remove those */ + if (unneeded && !MAPTST(&userinstalled, p - installed->start)) + continue; + MAPSET(&im, p); + } + MAPSET(&installedm, SYSTEMSOLVABLE); + MAPSET(&im, SYSTEMSOLVABLE); + + if (!unneeded && solv->cleandeps_updatepkgs) + { + /* find update "seeds" */ + queue_init(&updatepkgs_filtered); + find_update_seeds(solv, &updatepkgs_filtered, &userinstalled); + } + +#ifdef CLEANDEPSDEBUG + printf("REMOVE PASS\n"); +#endif + + for (;;) + { + if (!iq.count) + { + if (unneeded) + break; + /* supplements pass */ + for (ip = installed->start; ip < installed->end; ip++) + { + if (!MAPTST(&installedm, ip)) + continue; + s = pool->solvables + ip; + if (!s->supplements) + continue; + if (!MAPTST(&im, ip)) + continue; + if (MAPTST(&userinstalled, ip - installed->start)) + continue; + supp = s->repo->idarraydata + s->supplements; + while ((sup = *supp++) != 0) + if (solver_dep_possible(solv, sup, &im)) + break; + if (!sup) + { + supp = s->repo->idarraydata + s->supplements; + while ((sup = *supp++) != 0) + if (solver_dep_possible(solv, sup, &installedm) || (xsuppq.count && queue_contains(&xsuppq, sup))) + { + /* no longer supplemented, also erase */ + int iqcount = iq.count; + /* pin packages, see comment above dep_pkgcheck */ + dep_pkgcheck(solv, sup, &im, &iq); + for (i = iqcount; i < iq.count; i++) + { + Id pqp = iq.elements[i]; + if (pool->solvables[pqp].repo == installed) + MAPSET(&userinstalled, pqp - installed->start); + } + queue_truncate(&iq, iqcount); +#ifdef CLEANDEPSDEBUG + printf("%s supplemented [%s]\n", pool_solvid2str(pool, ip), pool_dep2str(pool, sup)); +#endif + queue_push(&iq, ip); + } + } + } + if (!iq.count) + break; /* no supplementing package found, we're done */ + } + ip = queue_shift(&iq); + s = pool->solvables + ip; + if (!MAPTST(&im, ip)) + continue; + if (!MAPTST(&installedm, ip)) + continue; + if (s->repo == installed && MAPTST(&userinstalled, ip - installed->start)) + continue; + MAPCLR(&im, ip); +#ifdef CLEANDEPSDEBUG + printf("removing %s\n", pool_solvable2str(pool, s)); +#endif + if (s->requires) + { + reqp = s->repo->idarraydata + s->requires; + while ((req = *reqp++) != 0) + { + if (req == SOLVABLE_PREREQMARKER) + continue; +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, req)) + { + complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq); + continue; + } +#endif + FOR_PROVIDES(p, pp, req) + { + if (p != SYSTEMSOLVABLE && MAPTST(&im, p)) + { +#ifdef CLEANDEPSDEBUG + printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); +#endif + queue_push(&iq, p); + } + } + } + } + if (s->recommends) + { + reqp = s->repo->idarraydata + s->recommends; + while ((req = *reqp++) != 0) + { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, req)) + { + complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq); + continue; + } +#endif + FOR_PROVIDES(p, pp, req) + { + if (p != SYSTEMSOLVABLE && MAPTST(&im, p)) + { +#ifdef CLEANDEPSDEBUG + printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); +#endif + queue_push(&iq, p); + } + } + } + } + } + + /* turn userinstalled into remove set for pruning */ + map_empty(&userinstalled); + for (rid = solv->jobrules; rid < solv->jobrules_end; rid++) + { + r = solv->rules + rid; + if (r->p >= 0 || r->d != 0 || r->w2 != 0) + continue; /* disabled or not erase */ + p = -r->p; + MAPCLR(&im, p); + if (pool->solvables[p].repo == installed) + MAPSET(&userinstalled, p - installed->start); + } + if (!unneeded && solv->cleandeps_updatepkgs) + { + for (i = 0; i < solv->cleandeps_updatepkgs->count; i++) + { + p = solv->cleandeps_updatepkgs->elements[i]; + if (pool->solvables[p].repo == installed) + MAPSET(&userinstalled, p - installed->start); + } + } + MAPSET(&im, SYSTEMSOLVABLE); /* in case we cleared it above */ + for (p = installed->start; p < installed->end; p++) + if (MAPTST(&im, p)) + queue_push(&iq, p); + for (rid = solv->jobrules; rid < solv->jobrules_end; rid++) + { + r = solv->rules + rid; + if (r->d < 0) + continue; + FOR_RULELITERALS(p, jp, r) + if (p > 0) + queue_push(&iq, p); + } + /* also put directly addressed packages on the install queue + * so we can mark patterns as installed */ + for (i = 0; i < job->count; i += 2) + { + how = job->elements[i]; + if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED) + { + what = job->elements[i + 1]; + select = how & SOLVER_SELECTMASK; + if (select == SOLVER_SOLVABLE && pool->solvables[what].repo != installed) + queue_push(&iq, what); + } + } + +#ifdef CLEANDEPSDEBUG + printf("ADDBACK PASS\n"); +#endif + for (;;) + { + if (!iq.count) + { + /* supplements pass */ + for (ip = installed->start; ip < installed->end; ip++) + { + if (!MAPTST(&installedm, ip)) + continue; + if (MAPTST(&userinstalled, ip - installed->start)) + continue; + s = pool->solvables + ip; + if (!s->supplements) + continue; + if (MAPTST(&im, ip)) + continue; + supp = s->repo->idarraydata + s->supplements; + while ((sup = *supp++) != 0) + if (solver_dep_possible(solv, sup, &im)) + break; + if (sup) + { +#ifdef CLEANDEPSDEBUG + printf("%s supplemented\n", pool_solvid2str(pool, ip)); +#endif + MAPSET(&im, ip); + queue_push(&iq, ip); + } + } + if (!iq.count) + break; + } + ip = queue_shift(&iq); + s = pool->solvables + ip; +#ifdef CLEANDEPSDEBUG + printf("adding back %s\n", pool_solvable2str(pool, s)); +#endif + if (s->repo == installed && pool->implicitobsoleteusescolors) + { + Id a, bestarch = 0; + FOR_PROVIDES(p, pp, s->name) + { + Solvable *ps = pool->solvables + p; + if (ps->name != s->name || ps->repo == installed) + continue; + a = ps->arch; + a = (a <= pool->lastarch) ? pool->id2arch[a] : 0; + if (a && a != 1 && (!bestarch || a < bestarch)) + bestarch = a; + } + if (bestarch && (s->arch > pool->lastarch || pool->id2arch[s->arch] != bestarch)) + { + FOR_PROVIDES(p, pp, s->name) + { + Solvable *ps = pool->solvables + p; + if (ps->repo == installed && ps->name == s->name && ps->evr == s->evr && ps->arch != s->arch && ps->arch < pool->lastarch && pool->id2arch[ps->arch] == bestarch) + if (!MAPTST(&im, p)) + { +#ifdef CLEANDEPSDEBUG + printf("%s lockstep %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); +#endif + MAPSET(&im, p); + queue_push(&iq, p); + } + } + } + } + if (s->requires) + { + reqp = s->repo->idarraydata + s->requires; + while ((req = *reqp++) != 0) + { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, req)) + { + complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled); + continue; + } +#endif + FOR_PROVIDES(p, pp, req) + if (p == ip) + break; + if (p) + continue; + FOR_PROVIDES(p, pp, req) + { + if (MAPTST(&im, p)) + continue; + if (MAPTST(&installedm, p)) + { + if (p == ip) + continue; + if (MAPTST(&userinstalled, p - installed->start)) + continue; +#ifdef CLEANDEPSDEBUG + printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); +#endif + MAPSET(&im, p); + queue_push(&iq, p); + } + } + } + } + if (s->recommends) + { + reqp = s->repo->idarraydata + s->recommends; + while ((req = *reqp++) != 0) + { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, req)) + { + complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled); + continue; + } +#endif + FOR_PROVIDES(p, pp, req) + if (p == ip) + break; + if (p) + continue; + FOR_PROVIDES(p, pp, req) + { + if (MAPTST(&im, p)) + continue; + if (MAPTST(&installedm, p)) + { + if (p == ip) + continue; + if (MAPTST(&userinstalled, p - installed->start)) + continue; +#ifdef CLEANDEPSDEBUG + printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); +#endif + MAPSET(&im, p); + queue_push(&iq, p); + } + } + } + } + } + + queue_free(&iq); + /* make sure the updatepkgs and mistakes are not in the cleandeps map */ + if (!unneeded && solv->cleandeps_updatepkgs) + { + for (i = 0; i < updatepkgs_filtered.count; i++) + MAPSET(&im, updatepkgs_filtered.elements[i]); + queue_free(&updatepkgs_filtered); + } + if (solv->cleandeps_mistakes) + for (i = 0; i < solv->cleandeps_mistakes->count; i++) + MAPSET(&im, solv->cleandeps_mistakes->elements[i]); + /* also remove original iq packages */ + for (i = 0; i < iqcopy.count; i++) + MAPSET(&im, iqcopy.elements[i]); + queue_free(&iqcopy); + for (p = installed->start; p < installed->end; p++) + { + if (pool->solvables[p].repo != installed) + continue; + if (pool->considered && !MAPTST(pool->considered, p)) + continue; + if (!MAPTST(&im, p)) + MAPSET(cleandepsmap, p - installed->start); + } + map_free(&im); + map_free(&installedm); + map_free(&userinstalled); + queue_free(&xsuppq); +#ifdef CLEANDEPSDEBUG + printf("=== final cleandeps map:\n"); + for (p = installed->start; p < installed->end; p++) + if (MAPTST(cleandepsmap, p - installed->start)) + printf(" - %s\n", pool_solvid2str(pool, p)); +#endif +} + +void +solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered) +{ + Repo *installed = solv->installed; + int i; + Map cleandepsmap; + + queue_empty(unneededq); + if (!installed || installed->end == installed->start) + return; + + map_init(&cleandepsmap, installed->end - installed->start); + solver_createcleandepsmap(solv, &cleandepsmap, 1); + for (i = installed->start; i < installed->end; i++) + if (MAPTST(&cleandepsmap, i - installed->start)) + queue_push(unneededq, i); + + if (filtered) + filter_unneeded(solv, unneededq, &cleandepsmap, 0); + map_free(&cleandepsmap); +} + +static void +add_cleandeps_mistake(Solver *solv, Id p) +{ + if (!solv->cleandeps_mistakes) + { + solv->cleandeps_mistakes = solv_calloc(1, sizeof(Queue)); + queue_init(solv->cleandeps_mistakes); + } + queue_push(solv->cleandeps_mistakes, p); + MAPCLR(&solv->cleandepsmap, p - solv->installed->start); + solver_reenablepolicyrules_cleandeps(solv, p); +} + +static inline int +cleandeps_rule_is_true(Solver *solv, Rule *r) +{ + Pool *pool = solv->pool; + Id p, pp; + FOR_RULELITERALS(p, pp, r) + if (p > 0 && solv->decisionmap[p] > 0) + return 1; + return 0; +} + +int +solver_check_cleandeps_mistakes(Solver *solv) +{ + Pool *pool = solv->pool; + Repo *installed = solv->installed; + Rule *fr; + int i, j, nj; + int mademistake = 0; + + if (!solv->cleandepsmap.size || !installed) + return 0; + /* check for mistakes */ + policy_update_recommendsmap(solv); + for (i = installed->start; i < installed->end; i++) + { + if (pool->solvables[i].repo != installed) + continue; + if (solv->decisionmap[i] > 0) + { + Id req, *reqp; + Solvable *s = pool->solvables + i; + /* kept package, check requires. we need to do this for things like requires(pre) */ + reqp = s->repo->idarraydata + s->requires; + while ((req = *reqp++) != 0) + { + Id p2, pp2; + FOR_PROVIDES(p2, pp2, req) + { + if (pool->solvables[p2].repo != installed) + continue; + if (p2 == i || solv->decisionmap[p2] > 0) + continue; + if (!MAPTST(&solv->cleandepsmap, p2 - installed->start)) + continue; + POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps requires mistake: %s %s %s\n", pool_solvid2str(pool, i), pool_dep2str(pool, req), pool_solvid2str(pool, p2)); + add_cleandeps_mistake(solv, p2); + mademistake = 1; + } + } + } + if (!MAPTST(&solv->cleandepsmap, i - installed->start)) + continue; + /* a mistake is when the featurerule is true but the updaterule is false */ + fr = solv->rules + solv->featurerules + (i - installed->start); + if (!fr->p) + fr = solv->rules + solv->updaterules + (i - installed->start); + if (!fr->p) + continue; + if (!cleandeps_rule_is_true(solv, fr)) + { + /* feature rule is not true, thus we cleandeps erased the package */ + /* check if the package is recommended/supplemented. if yes, we made a mistake. */ + if (!MAPTST(&solv->recommendsmap, i) && !solver_is_supplementing(solv, pool->solvables + i)) + continue; /* feature rule is not true */ + POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps recommends mistake: "); + solver_printruleclass(solv, SOLV_DEBUG_SOLVER, fr); + } + else + { + Rule *r = solv->rules + solv->updaterules + (i - installed->start); + if (!r->p || r == fr || cleandeps_rule_is_true(solv, r)) + { + /* update rule is true, check best rules */ + if (!solv->bestrules_pkg) + continue; + nj = solv->bestrules_end - solv->bestrules; + for (j = 0; j < nj; j++) + if (solv->bestrules_pkg[j] == i) + { + r = solv->rules + solv->bestrules + j; + if (!cleandeps_rule_is_true(solv, r)) + break; + } + if (j == nj) + continue; + } + POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps mistake: "); + solver_printruleclass(solv, SOLV_DEBUG_SOLVER, r); + POOL_DEBUG(SOLV_DEBUG_SOLVER, "feature rule: "); + solver_printruleclass(solv, SOLV_DEBUG_SOLVER, fr); + } + add_cleandeps_mistake(solv, i); + mademistake = 1; + } + return mademistake; +} diff --git a/src/libsolv.ver b/src/libsolv.ver index d65c0d6..337cd19 100644 --- a/src/libsolv.ver +++ b/src/libsolv.ver @@ -425,4 +425,5 @@ SOLV_1.0 { SOLV_1.1 { pool_best_solvables; + solver_get_cleandeps; } SOLV_1.0; diff --git a/src/order.c b/src/order.c index 4fe3e08..02e11d1 100644 --- a/src/order.c +++ b/src/order.c @@ -43,8 +43,10 @@ struct _TransactionOrderdata { #define TYPE_REQ_P (1<<2) #define TYPE_PREREQ_P (1<<3) -#define TYPE_REQ (1<<4) -#define TYPE_PREREQ (1<<5) +#define TYPE_REC (1<<4) + +#define TYPE_REQ (1<<5) +#define TYPE_PREREQ (1<<6) #define TYPE_CYCLETAIL (1<<16) #define TYPE_CYCLEHEAD (1<<17) @@ -245,19 +247,19 @@ addsolvableedges(struct orderdata *od, Solvable *s) { Transaction *trans = od->trans; Pool *pool = trans->pool; - Id req, *reqp, con, *conp; + Id req, *reqp, con, *conp, rec, *recp; Id p, p2, pp2; int i, j, pre, numins; Repo *installed = pool->installed; Solvable *s2; - Queue reqq; + Queue depq; int provbyinst; #if 0 printf("addsolvableedges %s\n", pool_solvable2str(pool, s)); #endif p = s - pool->solvables; - queue_init(&reqq); + queue_init(&depq); if (s->requires) { reqp = s->repo->idarraydata + s->requires; @@ -269,15 +271,7 @@ addsolvableedges(struct orderdata *od, Solvable *s) pre = TYPE_PREREQ; continue; } -#if 0 - if (pre != TYPE_PREREQ && installed && s->repo == installed) - { - /* ignore normal requires if we're getting obsoleted */ - if (trans->transaction_installed[p - pool->installed->start]) - continue; - } -#endif - queue_empty(&reqq); + queue_empty(&depq); numins = 0; /* number of packages to be installed providing it */ provbyinst = 0; /* provided by kept package */ FOR_PROVIDES(p2, pp2, req) @@ -285,19 +279,13 @@ addsolvableedges(struct orderdata *od, Solvable *s) s2 = pool->solvables + p2; if (p2 == p) { - reqq.count = 0; /* self provides */ + depq.count = 0; /* self provides */ break; } if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2)) { provbyinst = 1; -#if 0 - printf("IGNORE inst provides %s by %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s2)); - reqq.count = 0; /* provided by package that stays installed */ - break; -#else continue; -#endif } if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2)) continue; /* package stays uninstalled */ @@ -305,7 +293,7 @@ addsolvableedges(struct orderdata *od, Solvable *s) if (s->repo == installed) { /* s gets uninstalled */ - queue_pushunique(&reqq, p2); + queue_pushunique(&depq, p2); if (s2->repo != installed) numins++; } @@ -313,55 +301,53 @@ addsolvableedges(struct orderdata *od, Solvable *s) { if (s2->repo == installed) continue; /* s2 gets uninstalled */ - queue_pushunique(&reqq, p2); + queue_pushunique(&depq, p2); } } if (provbyinst) { /* prune to harmless ->inst edges */ - for (i = j = 0; i < reqq.count; i++) - if (pool->solvables[reqq.elements[i]].repo != installed) - reqq.elements[j++] = reqq.elements[i]; - reqq.count = j; + for (i = j = 0; i < depq.count; i++) + if (pool->solvables[depq.elements[i]].repo != installed) + depq.elements[j++] = depq.elements[i]; + depq.count = j; } - if (numins && reqq.count) + if (numins && depq.count) { if (s->repo == installed) { - for (i = 0; i < reqq.count; i++) + for (i = 0; i < depq.count; i++) { - if (pool->solvables[reqq.elements[i]].repo == installed) + if (pool->solvables[depq.elements[i]].repo == installed) { - for (j = 0; j < reqq.count; j++) + for (j = 0; j < depq.count; j++) { - if (pool->solvables[reqq.elements[j]].repo != installed) + if (pool->solvables[depq.elements[j]].repo != installed) { - if (trans->transaction_installed[reqq.elements[i] - pool->installed->start] == reqq.elements[j]) + if (trans->transaction_installed[depq.elements[i] - pool->installed->start] == depq.elements[j]) continue; /* no self edge */ #if 0 - printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, reqq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, reqq.elements[j])); + printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, depq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, depq.elements[j])); #endif - addedge(od, reqq.elements[i], reqq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P); + addedge(od, depq.elements[i], depq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P); } } } } } /* no mixed types, remove all deps on uninstalls */ - for (i = j = 0; i < reqq.count; i++) - if (pool->solvables[reqq.elements[i]].repo != installed) - reqq.elements[j++] = reqq.elements[i]; - reqq.count = j; + for (i = j = 0; i < depq.count; i++) + if (pool->solvables[depq.elements[i]].repo != installed) + depq.elements[j++] = depq.elements[i]; + depq.count = j; } - if (!reqq.count) - continue; - for (i = 0; i < reqq.count; i++) + for (i = 0; i < depq.count; i++) { - p2 = reqq.elements[i]; + p2 = depq.elements[i]; if (pool->solvables[p2].repo != installed) { - /* all elements of reqq are installs, thus have different TEs */ + /* all elements of depq are installs, thus have different TEs */ if (pool->solvables[p].repo != installed) { #if 0 @@ -435,13 +421,47 @@ addsolvableedges(struct orderdata *od, Solvable *s) } } } - if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &reqq) && reqq.count) + if (s->recommends && s->repo != installed) + { + recp = s->repo->idarraydata + s->recommends; + while ((rec = *recp++) != 0) + { + queue_empty(&depq); + FOR_PROVIDES(p2, pp2, rec) + { + s2 = pool->solvables + p2; + if (p2 == p) + { + depq.count = 0; /* self provides */ + break; + } + if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2)) + continue; + if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2)) + continue; /* package stays uninstalled */ + if (s2->repo != installed) + queue_pushunique(&depq, p2); + } + for (i = 0; i < depq.count; i++) + { + p2 = depq.elements[i]; + if (pool->solvables[p2].repo != installed) + { +#if 0 + printf("add recommends inst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, rec), pool_solvid2str(pool, p2)); +#endif + addedge(od, p, p2, TYPE_REC); + } + } + } + } + if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &depq) && depq.count) { /* we're getting deinstalled/updated. Try to do this before our * triggers are hit */ - for (i = 0; i < reqq.count; i++) + for (i = 0; i < depq.count; i++) { - Id tri = reqq.elements[i]; + Id tri = depq.elements[i]; FOR_PROVIDES(p2, pp2, tri) { if (p2 == p) @@ -462,7 +482,7 @@ addsolvableedges(struct orderdata *od, Solvable *s) } } } - queue_free(&reqq); + queue_free(&depq); } diff --git a/src/policy.c b/src/policy.c index 468be67..6f06101 100644 --- a/src/policy.c +++ b/src/policy.c @@ -145,7 +145,7 @@ solver_prune_installed_dup_packages(Solver *solv, Queue *plist) Solvable *s = pool->solvables + p; if (s->repo != pool->installed && s->repo->priority < bestprio) continue; - if (s->repo == pool->installed && (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))) + if (s->repo == pool->installed && (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))) { Id p2, pp2; int keepit = 0; @@ -181,7 +181,7 @@ static inline void solver_prune_to_highest_prio(Solver *solv, Queue *plist) { prune_to_highest_prio(solv->pool, plist); - if (plist->count > 1 && solv->pool->installed && (solv->dupmap_all || solv->dupinvolvedmap.size)) + if (plist->count > 1 && solv->pool->installed && (solv->dupinvolvedmap_all || solv->dupinvolvedmap.size)) solver_prune_installed_dup_packages(solv, plist); } @@ -1424,7 +1424,7 @@ policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore) { Pool *pool = solv->pool; int ret = 0; - int duppkg = solv->dupmap_all ? 1 : 0; + int duppkg = solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, is - pool->solvables)); if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !(duppkg ? solv->dup_allowdowngrade : solv->allowdowngrade)) { if (is->name == s->name && pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0) diff --git a/src/problems.c b/src/problems.c index a9bbdce..2ffb6f9 100644 --- a/src/problems.c +++ b/src/problems.c @@ -454,13 +454,6 @@ convertsolution(Solver *solv, Id why, Queue *solutionq) return; /* false alarm */ p = solv->installed->start + (why - solv->updaterules); - if (solv->dupmap_all && solv->rules[why].p != p && solv->decisionmap[p] > 0) - { - /* distupgrade case, allow to keep old package */ - queue_push(solutionq, SOLVER_SOLUTION_DISTUPGRADE); - queue_push(solutionq, p); - return; - } if (solv->decisionmap[p] > 0) return; /* false alarm, turned out we can keep the package */ rr = solv->rules + solv->featurerules + (why - solv->updaterules); diff --git a/src/rules.c b/src/rules.c index 5654330..b5f3e3e 100644 --- a/src/rules.c +++ b/src/rules.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2009, Novell Inc. + * Copyright (c) 2007-2017, SUSE Inc. * * This program is licensed under the BSD license, read LICENSE.BSD * for further information @@ -32,51 +32,6 @@ #define RULES_BLOCK 63 static void addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep); -static void solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded); - -/*------------------------------------------------------------------- - * Check if dependency is possible - * - * mirrors solver_dep_fulfilled but uses map m instead of the decisionmap. - * used in solver_addpkgrulesforweak and solver_createcleandepsmap. - */ - -static inline int -dep_possible(Solver *solv, Id dep, Map *m) -{ - Pool *pool = solv->pool; - Id p, pp; - - if (ISRELDEP(dep)) - { - Reldep *rd = GETRELDEP(pool, dep); - if (rd->flags >= 8) - { - if (rd->flags == REL_COND || rd->flags == REL_UNLESS) - return 1; - if (rd->flags == REL_AND) - { - if (!dep_possible(solv, rd->name, m)) - return 0; - return dep_possible(solv, rd->evr, m); - } - if (rd->flags == REL_OR) - { - if (dep_possible(solv, rd->name, m)) - return 1; - return dep_possible(solv, rd->evr, m); - } - if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES) - return solver_splitprovides(solv, rd->evr, m); - } - } - FOR_PROVIDES(p, pp, dep) - { - if (MAPTST(m, p)) - return 1; - } - return 0; -} static inline int is_otherproviders_dep(Pool *pool, Id dep) @@ -1205,7 +1160,7 @@ solver_addpkgrulesforweak(Solver *solv, Map *m) /* find possible supplements */ supp = s->repo->idarraydata + s->supplements; while ((sup = *supp++) != 0) - if (dep_possible(solv, sup, m)) + if (solver_dep_possible(solv, sup, m)) break; } @@ -1214,7 +1169,7 @@ solver_addpkgrulesforweak(Solver *solv, Map *m) { supp = s->repo->idarraydata + s->enhances; while ((sup = *supp++) != 0) - if (dep_possible(solv, sup, m)) + if (solver_dep_possible(solv, sup, m)) break; } /* if nothing found, goto next solvables */ @@ -1294,58 +1249,6 @@ dup_maykeepinstalled(Solver *solv, Solvable *s) } -static Id -finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs) -{ - Pool *pool = solv->pool; - int i, j; - - policy_findupdatepackages(solv, s, qs, 2); - if (qs->count) - { - /* remove installed packages we can't keep */ - for (i = j = 0; i < qs->count; i++) - { - Solvable *ns = pool->solvables + qs->elements[i]; - if (ns->repo == pool->installed && !dup_maykeepinstalled(solv, ns)) - continue; - qs->elements[j++] = qs->elements[i]; - } - queue_truncate(qs, j); - } - /* check if it is ok to keep the installed package */ - if (dup_maykeepinstalled(solv, s)) - return s - pool->solvables; - /* nope, it must be some other package */ - return -SYSTEMSOLVABLE; -} - -#if 0 -/* add packages from the dup repositories to the update candidates - * this isn't needed for the global dup mode as all packages are - * from dup repos in that case */ -static void -addduppackages(Solver *solv, Solvable *s, Queue *qs) -{ - Queue dupqs; - Id p, dupqsbuf[64]; - int i; - int oldnoupdateprovide = solv->noupdateprovide; - - queue_init_buffer(&dupqs, dupqsbuf, sizeof(dupqsbuf)/sizeof(*dupqsbuf)); - solv->noupdateprovide = 1; - policy_findupdatepackages(solv, s, &dupqs, 2); - solv->noupdateprovide = oldnoupdateprovide; - for (i = 0; i < dupqs.count; i++) - { - p = dupqs.elements[i]; - if (MAPTST(&solv->dupmap, p)) - queue_pushunique(qs, p); - } - queue_free(&dupqs); -} -#endif - /* stash away the original updaters for multiversion packages. We do this so that * we can update the package later */ static inline void @@ -1392,7 +1295,7 @@ solver_addfeaturerule(Solver *solv, Solvable *s) queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf)); p = s - pool->solvables; policy_findupdatepackages(solv, s, &qs, 1); - if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))) + if (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))) { if (!dup_maykeepinstalled(solv, s)) { @@ -1440,8 +1343,8 @@ solver_addupdaterule(Solver *solv, Solvable *s) Id p, d; Queue qs; Id qsbuf[64]; - int isorphaned = 0; Rule *r; + int dupinvolved = 0; p = s - pool->solvables; /* Orphan detection. We cheat by looking at the feature rule, which @@ -1464,12 +1367,10 @@ solver_addupdaterule(Solver *solv, Solvable *s) return; } - queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf)); /* find update candidates for 's' */ - if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))) - p = finddistupgradepackages(solv, s, &qs); - else - policy_findupdatepackages(solv, s, &qs, 0); + queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf)); + dupinvolved = solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)); + policy_findupdatepackages(solv, s, &qs, dupinvolved ? 2 : 0); if (qs.count && solv->multiversion.size) { @@ -1500,44 +1401,41 @@ solver_addupdaterule(Solver *solv, Solvable *s) } qs.elements[j++] = qs.elements[i]; } - if (j < qs.count) /* filtered at least one package? */ + + if (j == 0 && dupinvolved && !dup_maykeepinstalled(solv, s)) { - if (j == 0 && p == -SYSTEMSOLVABLE) + /* this is a multiversion orphan */ + queue_push(&solv->orphaned, p); + set_specialupdaters(solv, s, d); + if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start)))) { - /* this is a multiversion orphan */ - queue_push(&solv->orphaned, s - pool->solvables); - set_specialupdaters(solv, s, d); - if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start)))) - { - /* we need to keep the orphan */ - queue_free(&qs); - solver_addrule(solv, s - pool->solvables, 0, 0); - return; - } - /* we can drop it as long as we update */ - isorphaned = 1; - j = qs.count; /* force the update */ + /* we need to keep the orphan */ + queue_free(&qs); + solver_addrule(solv, p, 0, 0); + return; } - else if (d && (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, s - pool->solvables - solv->installed->start)))) + /* we can drop it as long as we update */ + j = qs.count; + } + + if (j < qs.count) /* filtered at least one package? */ + { + if (d && (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p - solv->installed->start)))) { /* non-orphan multiversion package, set special updaters if we want an update */ set_specialupdaters(solv, s, d); } qs.count = j; } - else if (p != -SYSTEMSOLVABLE) + else { /* could fallthrough, but then we would do pool_queuetowhatprovides twice */ queue_free(&qs); - solver_addrule(solv, s - pool->solvables, 0, d); /* allow update of s */ + solver_addrule(solv, p, 0, d); /* allow update of s */ return; } } } - if (!isorphaned && p == -SYSTEMSOLVABLE && qs.count && solv->dupmap.size) - p = s - pool->solvables; /* let the dup rules sort it out */ - if (qs.count && p == -SYSTEMSOLVABLE) - p = queue_shift(&qs); if (qs.count > 1) { d = pool_queuetowhatprovides(pool, &qs); @@ -1664,9 +1562,10 @@ solver_addinfarchrules(Solver *solv, Map *addedmap) a = (a <= pool->lastarch) ? pool->id2arch[a] : 0; if (a != 1 && installed && ps->repo == installed) { - if (!solv->dupmap_all && !(solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))) - queue_pushunique(&allowedarchs, ps->arch); /* also ok to keep this architecture */ - continue; /* ignore installed solvables when calculating the best arch */ + if (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))) + continue; + queue_pushunique(&allowedarchs, ps->arch); /* also ok to keep this architecture */ + continue; /* but ignore installed solvables when calculating the best arch */ } if (a && a != 1 && (!bestarch || a < bestarch)) { @@ -1692,7 +1591,7 @@ solver_addinfarchrules(Solver *solv, Map *addedmap) ps = pool->solvables + p; if (ps->name != s->name || ps->repo != installed || !MAPTST(addedmap, p)) continue; - if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))) + if (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))) continue; a = ps->arch; a = (a <= pool->lastarch) ? pool->id2arch[a] : 0; @@ -1834,7 +1733,7 @@ reenableinfarchrule(Solver *solv, Id name) ***/ static inline void -add_cleandeps_package(Solver *solv, Id p) +add_cleandeps_updatepkg(Solver *solv, Id p) { if (!solv->cleandeps_updatepkgs) { @@ -1852,6 +1751,9 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted) Repo *installed = solv->installed; Id pi, pip, obs, *obsp; + if (!solv->dupinvolvedmap.size) + map_grow(&solv->dupinvolvedmap, pool->nsolvables); + MAPSET(&solv->dupinvolvedmap, p); if (targeted) MAPSET(&solv->dupmap, p); @@ -1868,14 +1770,14 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted) if (pool->solvables[pi2].repo != installed) MAPSET(&solv->dupinvolvedmap, pi2); } - if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0) + if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0 && !solv->bestupdatemap_all) { if (!solv->bestupdatemap.size) map_grow(&solv->bestupdatemap, installed->end - installed->start); MAPSET(&solv->bestupdatemap, pi - installed->start); } if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0) - add_cleandeps_package(solv, pi); + add_cleandeps_updatepkg(solv, pi); if (!targeted && ps->repo != installed) MAPSET(&solv->dupmap, pi); } @@ -1913,19 +1815,23 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted) if (pool->solvables[pi2].repo != installed) MAPSET(&solv->dupinvolvedmap, pi2); } - if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0) + if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0 && !solv->bestupdatemap_all) { if (!solv->bestupdatemap.size) map_grow(&solv->bestupdatemap, installed->end - installed->start); MAPSET(&solv->bestupdatemap, pi - installed->start); } if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0) - add_cleandeps_package(solv, pi); + add_cleandeps_updatepkg(solv, pi); } } } } +/* create the two dupmaps: + * - dupmap: packages in that map are good to install/keep + * - dupinvolvedmap: packages are subject to dup mode + */ void solver_createdupmaps(Solver *solv) { @@ -1937,7 +1843,8 @@ solver_createdupmaps(Solver *solv) int i, targeted; map_init(&solv->dupmap, pool->nsolvables); - map_init(&solv->dupinvolvedmap, pool->nsolvables); + solv->dupinvolvedmap_all = 0; + map_init(&solv->dupinvolvedmap, 0); for (i = 0; i < job->count; i += 2) { how = job->elements[i]; @@ -1966,11 +1873,22 @@ solver_createdupmaps(Solver *solv) } else if (select == SOLVER_SOLVABLE_ALL) { + solv->dupinvolvedmap_all = 1; FOR_POOL_SOLVABLES(p) { - MAPSET(&solv->dupinvolvedmap, p); - if (installed && pool->solvables[p].repo != installed) - MAPSET(&solv->dupmap, p); + Solvable *s = pool->solvables + p; + if (!s->repo || s->repo == installed) + continue; + if (!pool_installable(pool, s)) + continue; + MAPSET(&solv->dupmap, p); + } + if ((how & SOLVER_FORCEBEST) != 0) + solv->bestupdatemap_all = 1; + if ((how & SOLVER_CLEANDEPS) != 0 && installed) + { + FOR_REPO_SOLVABLES(installed, p, s) + add_cleandeps_updatepkg(solv, p); } } else @@ -2002,7 +1920,8 @@ solver_createdupmaps(Solver *solv) break; } } - MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE); + if (solv->dupinvolvedmap.size) + MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE); } void @@ -2024,6 +1943,8 @@ solver_addduprules(Solver *solv, Map *addedmap) Rule *r; solv->duprules = solv->nrules; + if (solv->dupinvolvedmap_all) + solv->updatemap_all = 1; for (i = 1; i < pool->nsolvables; i++) { if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i)) @@ -2039,13 +1960,16 @@ solver_addduprules(Solver *solv, Map *addedmap) first = 0; if (first) break; - if (!MAPTST(&solv->dupinvolvedmap, p)) + if (!solv->dupinvolvedmap_all && !MAPTST(&solv->dupinvolvedmap, p)) continue; if (installed && ps->repo == installed) { - if (!solv->updatemap.size) - map_grow(&solv->updatemap, installed->end - installed->start); - MAPSET(&solv->updatemap, p - installed->start); + if (!solv->updatemap_all) + { + if (!solv->updatemap.size) + map_grow(&solv->updatemap, installed->end - installed->start); + MAPSET(&solv->updatemap, p - installed->start); + } if (!MAPTST(&solv->dupmap, p)) { Id ip, ipp; @@ -2060,24 +1984,35 @@ solver_addduprules(Solver *solv, Map *addedmap) } if (ip) { - /* ok, found a good one. we may keep this package. */ + /* ok, identical to a good one. we may keep this package. */ MAPSET(&solv->dupmap, p); /* for best rules processing */ continue; } + /* check if it's orphaned. If yes, we may keep it */ r = solv->rules + solv->updaterules + (p - installed->start); if (!r->p) - r = solv->rules + solv->featurerules + (p - installed->start); - if (r->p && solv->specialupdaters && solv->specialupdaters[p - installed->start]) + r = solv->rules + solv->featurerules + (p - installed->start); + if (!r->p) + { + /* no update/feature rule, this is an orphan */ + MAPSET(&solv->dupmap, p); /* for best rules processing */ + continue; + } + if (solv->specialupdaters && solv->specialupdaters[p - installed->start]) { /* this is a multiversion orphan, we're good if an update is installed */ solver_addrule(solv, -p, 0, solv->specialupdaters[p - installed->start]); continue; } - if (!r->p || (r->p == p && !r->d && !r->w2)) + if (r->p == p && !r->d && !r->w2) { - /* this is an orphan */ - MAPSET(&solv->dupmap, p); /* for best rules processing */ - continue; + r = solv->rules + solv->featurerules + (p - installed->start); + if (!r->p || (!r->d && !r->w2)) + { + /* this is an orphan */ + MAPSET(&solv->dupmap, p); /* for best rules processing */ + continue; + } } solver_addrule(solv, -p, 0, 0); /* no match, sorry */ } @@ -2137,120 +2072,6 @@ reenableduprule(Solver *solv, Id name) #define DISABLE_INFARCH 2 #define DISABLE_DUP 3 -/* - * add all installed packages that package p obsoletes to Queue q. - * Package p is not installed. Also, we know that if - * solv->keepexplicitobsoletes is not set, p is not in the multiversion map. - * Entries may get added multiple times. - */ -static void -add_obsoletes(Solver *solv, Id p, Queue *q) -{ - Pool *pool = solv->pool; - Repo *installed = solv->installed; - Id p2, pp2; - Solvable *s = pool->solvables + p; - Id obs, *obsp; - Id lastp2 = 0; - - if (!solv->keepexplicitobsoletes || !(solv->multiversion.size && MAPTST(&solv->multiversion, p))) - { - FOR_PROVIDES(p2, pp2, s->name) - { - Solvable *ps = pool->solvables + p2; - if (ps->repo != installed) - continue; - if (!pool->implicitobsoleteusesprovides && ps->name != s->name) - continue; - if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps)) - continue; - queue_push(q, p2); - lastp2 = p2; - } - } - if (!s->obsoletes) - return; - obsp = s->repo->idarraydata + s->obsoletes; - while ((obs = *obsp++) != 0) - FOR_PROVIDES(p2, pp2, obs) - { - Solvable *ps = pool->solvables + p2; - if (ps->repo != installed) - continue; - if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) - continue; - if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) - continue; - if (p2 == lastp2) - continue; - queue_push(q, p2); - lastp2 = p2; - } -} - -/* - * Call add_obsoletes and intersect the result with the - * elements in Queue q starting at qstart. - * Assumes that it's the first call if qstart == q->count. - * May use auxillary map m for the intersection process, all - * elements of q starting at qstart must have their bit cleared. - * (This is also true after the function returns.) - */ -static void -intersect_obsoletes(Solver *solv, Id p, Queue *q, int qstart, Map *m) -{ - int i, j; - int qcount = q->count; - - add_obsoletes(solv, p, q); - if (qcount == qstart) - return; /* first call */ - if (qcount == q->count) - j = qstart; - else if (qcount == qstart + 1) - { - /* easy if there's just one element */ - j = qstart; - for (i = qcount; i < q->count; i++) - if (q->elements[i] == q->elements[qstart]) - { - j++; /* keep the element */ - break; - } - } - else if (!m->size && q->count - qstart <= 8) - { - /* faster than a map most of the time */ - int k; - for (i = j = qstart; i < qcount; i++) - { - Id ip = q->elements[i]; - for (k = qcount; k < q->count; k++) - if (q->elements[k] == ip) - { - q->elements[j++] = ip; - break; - } - } - } - else - { - /* for the really pathologic cases we use the map */ - Repo *installed = solv->installed; - if (!m->size) - map_init(m, installed->end - installed->start); - for (i = qcount; i < q->count; i++) - MAPSET(m, q->elements[i] - installed->start); - for (i = j = qstart; i < qcount; i++) - if (MAPTST(m, q->elements[i] - installed->start)) - { - MAPCLR(m, q->elements[i] - installed->start); - q->elements[j++] = q->elements[i]; - } - } - queue_truncate(q, j); -} - static void jobtodisablelist(Solver *solv, Id how, Id what, Queue *q) { @@ -2343,30 +2164,19 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q) return; /* now the hard part: disable some update rules */ - /* first check if we have multiversion or installed packages in the job */ - i = j = 0; + /* first check if we have installed or multiversion packages in the job */ FOR_JOB_SELECT(p, pp, select, what) { if (pool->solvables[p].repo == installed) - j = p; - else if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes) return; - i++; - } - if (j) /* have installed packages */ - { - /* this is for dupmap_all jobs, it can go away if we create - * duprules for them */ - if (i == 1 && (set & SOLVER_SETREPO) != 0) - queue_push2(q, DISABLE_UPDATE, j); - return; + if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes) + return; } - omap.size = 0; qstart = q->count; FOR_JOB_SELECT(p, pp, select, what) { - intersect_obsoletes(solv, p, q, qstart, &omap); + solver_intersect_obsoleted(solv, p, q, qstart, &omap); if (q->count == qstart) break; } @@ -3439,11 +3249,6 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs) int i, oldcnt; solv->bestrules = solv->nrules; - if (!installed) - { - solv->bestrules_end = solv->nrules; - return; - } queue_init(&q); queue_init(&q2); queue_init(&r2pkg); @@ -3483,7 +3288,7 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs) } } - if (solv->bestupdatemap_all || solv->bestupdatemap.size) + if (installed && (solv->bestupdatemap_all || solv->bestupdatemap.size)) { Map m; @@ -3810,1125 +3615,6 @@ for (j = 0; j < qq.count; j++) POOL_DEBUG(SOLV_DEBUG_STATS, "yumobs rule creation took %d ms\n", solv_timems(now)); } -#undef CLEANDEPSDEBUG - -/* - * This functions collects all packages that are looked at - * when a dependency is checked. We need it to "pin" installed - * packages when removing a supplemented package in createcleandepsmap. - * Here's an not uncommon example: - * A contains "Supplements: packageand(B, C)" - * B contains "Requires: A" - * Now if we remove C, the supplements is no longer true, - * thus we also remove A. Without the dep_pkgcheck function, we - * would now also remove B, but this is wrong, as adding back - * C doesn't make the supplements true again. Thus we "pin" B - * when we remove A. - * There's probably a better way to do this, but I haven't come - * up with it yet ;) - */ -static inline void -dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q) -{ - Pool *pool = solv->pool; - Id p, pp; - - if (ISRELDEP(dep)) - { - Reldep *rd = GETRELDEP(pool, dep); - if (rd->flags >= 8) - { - if (rd->flags == REL_AND) - { - dep_pkgcheck(solv, rd->name, m, q); - dep_pkgcheck(solv, rd->evr, m, q); - return; - } - if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES) - return; - } - } - FOR_PROVIDES(p, pp, dep) - if (!m || MAPTST(m, p)) - queue_push(q, p); -} - -static int -check_xsupp(Solver *solv, Queue *depq, Id dep) -{ - Pool *pool = solv->pool; - Id p, pp; - - if (ISRELDEP(dep)) - { - Reldep *rd = GETRELDEP(pool, dep); - if (rd->flags >= 8) - { - if (rd->flags == REL_AND) - { - if (!check_xsupp(solv, depq, rd->name)) - return 0; - return check_xsupp(solv, depq, rd->evr); - } - if (rd->flags == REL_OR) - { - if (check_xsupp(solv, depq, rd->name)) - return 1; - return check_xsupp(solv, depq, rd->evr); - } - if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES) -#if 0 - return solver_splitprovides(solv, rd->evr); -#else - return 0; -#endif - } - if (depq && rd->flags == REL_NAMESPACE) - { - int i; - for (i = 0; i < depq->count; i++) - if (depq->elements[i] == dep || depq->elements[i] == rd->name) - return 1; - } - } - FOR_PROVIDES(p, pp, dep) - if (p == SYSTEMSOLVABLE || pool->solvables[p].repo == solv->installed) - return 1; - return 0; -} - -static inline int -queue_contains(Queue *q, Id id) -{ - int i; - for (i = 0; i < q->count; i++) - if (q->elements[i] == id) - return 1; - return 0; -} - -#ifdef ENABLE_COMPLEX_DEPS -static void -complex_cleandeps_remove(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq) -{ - int i; - Queue dq; - Id p; - - queue_init(&dq); - i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND); - if (i == 0 || i == 1) - { - queue_free(&dq); - return; - } - for (i = 0; i < dq.count; i++) - { - for (; (p = dq.elements[i]) != 0; i++) - { - if (p < 0) - { - if (!MAPTST(installedm, -p)) - break; - continue; - } - if (p != SYSTEMSOLVABLE && MAPTST(im, p)) - { -#ifdef CLEANDEPSDEBUG - printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); -#endif - queue_push(iq, p); - } - } - while (dq.elements[i]) - i++; - } - queue_free(&dq); -} - -static void -complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq, Map *userinstalled) -{ - int i, blk; - Queue dq; - Id p; - - queue_init(&dq); - i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND); - if (i == 0 || i == 1) - { - queue_free(&dq); - return; - } - for (i = 0; i < dq.count; i++) - { - blk = i; - for (; (p = dq.elements[i]) != 0; i++) - { - if (p < 0) - { - if (!MAPTST(installedm, -p)) - break; - } - else if (p == ip) - break; - } - if (!p) - { - for (i = blk; (p = dq.elements[i]) != 0; i++) - { - if (p < 0) - continue; - if (MAPTST(im, p)) - continue; - if (!MAPTST(installedm, p)) - continue; - if (p == ip || MAPTST(userinstalled, p - pool->installed->start)) - continue; -#ifdef CLEANDEPSDEBUG - printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); -#endif - MAPSET(im, p); - queue_push(iq, p); - } - } - while (dq.elements[i]) - i++; - } - queue_free(&dq); -} - -#endif - -/* - * Find all installed packages that are no longer - * needed regarding the current solver job. - * - * The algorithm is: - * - remove pass: remove all packages that could have - * been dragged in by the obsoleted packages. - * i.e. if package A is obsolete and contains "Requires: B", - * also remove B, as installing A will have pulled in B. - * after this pass, we have a set of still installed packages - * with broken dependencies. - * - add back pass: - * now add back all packages that the still installed packages - * require. - * - * The cleandeps packages are the packages removed in the first - * pass and not added back in the second pass. - * - * If we search for unneeded packages (unneeded is true), we - * simply remove all packages except the userinstalled ones in - * the first pass. - */ -static void -solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded) -{ - Pool *pool = solv->pool; - Repo *installed = solv->installed; - Queue *job = &solv->job; - Map userinstalled; - Map im; - Map installedm; - Rule *r; - Id rid, how, what, select; - Id p, pp, ip, jp; - Id req, *reqp, sup, *supp; - Solvable *s; - Queue iq, iqcopy, xsuppq; - int i; - - map_empty(cleandepsmap); - if (!installed || installed->end == installed->start) - return; - map_init(&userinstalled, installed->end - installed->start); - map_init(&im, pool->nsolvables); - map_init(&installedm, pool->nsolvables); - queue_init(&iq); - queue_init(&xsuppq); - - for (i = 0; i < job->count; i += 2) - { - how = job->elements[i]; - if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED) - { - what = job->elements[i + 1]; - select = how & SOLVER_SELECTMASK; - if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid)) - { - FOR_REPO_SOLVABLES(installed, p, s) - MAPSET(&userinstalled, p - installed->start); - } - FOR_JOB_SELECT(p, pp, select, what) - if (pool->solvables[p].repo == installed) - MAPSET(&userinstalled, p - installed->start); - } - if ((how & (SOLVER_JOBMASK | SOLVER_SELECTMASK)) == (SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES)) - { - what = job->elements[i + 1]; - if (ISRELDEP(what)) - { - Reldep *rd = GETRELDEP(pool, what); - if (rd->flags != REL_NAMESPACE) - continue; - if (rd->evr == 0) - { - queue_pushunique(&iq, rd->name); - continue; - } - FOR_PROVIDES(p, pp, what) - if (p) - break; - if (p) - continue; - queue_pushunique(&iq, what); - } - } - } - - /* have special namespace cleandeps erases */ - if (iq.count) - { - for (ip = installed->start; ip < installed->end; ip++) - { - s = pool->solvables + ip; - if (s->repo != installed) - continue; - if (!s->supplements) - continue; - supp = s->repo->idarraydata + s->supplements; - while ((sup = *supp++) != 0) - if (ISRELDEP(sup) && check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup)) - { -#ifdef CLEANDEPSDEBUG - printf("xsupp %s from %s\n", pool_dep2str(pool, sup), pool_solvid2str(pool, ip)); -#endif - queue_pushunique(&xsuppq, sup); - } - } - queue_empty(&iq); - } - - /* also add visible patterns to userinstalled for openSUSE */ - if (1) - { - Dataiterator di; - dataiterator_init(&di, pool, 0, 0, SOLVABLE_ISVISIBLE, 0, 0); - while (dataiterator_step(&di)) - { - Id *dp; - if (di.solvid <= 0) - continue; - s = pool->solvables + di.solvid; - if (!s->repo || !s->requires) - continue; - if (s->repo != installed && !pool_installable(pool, s)) - continue; - if (strncmp(pool_id2str(pool, s->name), "pattern:", 8) != 0) - continue; - dp = s->repo->idarraydata + s->requires; - for (dp = s->repo->idarraydata + s->requires; *dp; dp++) - FOR_PROVIDES(p, pp, *dp) - if (pool->solvables[p].repo == installed) - { - if (strncmp(pool_id2str(pool, pool->solvables[p].name), "pattern", 7) != 0) - continue; - MAPSET(&userinstalled, p - installed->start); - } - } - dataiterator_free(&di); - } - if (1) - { - /* all products and their buddies are userinstalled */ - for (p = installed->start; p < installed->end; p++) - { - Solvable *s = pool->solvables + p; - if (s->repo != installed) - continue; - if (!strncmp("product:", pool_id2str(pool, s->name), 8)) - { - MAPSET(&userinstalled, p - installed->start); -#ifdef ENABLE_LINKED_PKGS - if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1) - { - Id buddy = solv->instbuddy[p - installed->start]; - if (buddy >= installed->start && buddy < installed->end) - MAPSET(&userinstalled, buddy - installed->start); - } -#endif - } - } - } - - /* add all positive elements (e.g. locks) to "userinstalled" */ - for (rid = solv->jobrules; rid < solv->jobrules_end; rid++) - { - r = solv->rules + rid; - if (r->d < 0) - continue; - i = solv->ruletojob.elements[rid - solv->jobrules]; - if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS) - continue; - FOR_RULELITERALS(p, jp, r) - if (p > 0 && pool->solvables[p].repo == installed) - MAPSET(&userinstalled, p - installed->start); - } - - /* add all cleandeps candidates to iq */ - for (rid = solv->jobrules; rid < solv->jobrules_end; rid++) - { - r = solv->rules + rid; - if (r->d < 0) /* disabled? */ - continue; - if (r->d == 0 && r->p < 0 && r->w2 == 0) /* negative assertion (erase job)? */ - { - p = -r->p; - if (pool->solvables[p].repo != installed) - continue; - MAPCLR(&userinstalled, p - installed->start); - if (unneeded) - continue; - i = solv->ruletojob.elements[rid - solv->jobrules]; - how = job->elements[i]; - if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS)) - queue_push(&iq, p); - } - else if (r->p > 0) /* install job */ - { - if (unneeded) - continue; - i = solv->ruletojob.elements[rid - solv->jobrules]; - if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS) - { - /* check if the literals all obsolete some installed package */ - Map om; - int iqstart; - - /* just one installed literal */ - if (r->d == 0 && r->w2 == 0 && pool->solvables[r->p].repo == installed) - continue; - /* multiversion is bad */ - if (solv->multiversion.size && !solv->keepexplicitobsoletes) - { - FOR_RULELITERALS(p, jp, r) - if (MAPTST(&solv->multiversion, p)) - break; - if (p) - continue; - } - - om.size = 0; - iqstart = iq.count; - FOR_RULELITERALS(p, jp, r) - { - if (p < 0) - { - queue_truncate(&iq, iqstart); /* abort */ - break; - } - if (pool->solvables[p].repo == installed) - { - if (iq.count == iqstart) - queue_push(&iq, p); - else - { - for (i = iqstart; i < iq.count; i++) - if (iq.elements[i] == p) - break; - queue_truncate(&iq, iqstart); - if (i < iq.count) - queue_push(&iq, p); - } - } - else - intersect_obsoletes(solv, p, &iq, iqstart, &om); - if (iq.count == iqstart) - break; - } - if (om.size) - map_free(&om); - } - } - } - queue_init_clone(&iqcopy, &iq); - - if (!unneeded) - { - if (solv->cleandeps_updatepkgs) - for (i = 0; i < solv->cleandeps_updatepkgs->count; i++) - queue_push(&iq, solv->cleandeps_updatepkgs->elements[i]); - } - - if (unneeded) - queue_empty(&iq); /* just in case... */ - - /* clear userinstalled bit for the packages we really want to delete/update */ - for (i = 0; i < iq.count; i++) - { - p = iq.elements[i]; - if (pool->solvables[p].repo != installed) - continue; - MAPCLR(&userinstalled, p - installed->start); - } - - for (p = installed->start; p < installed->end; p++) - { - if (pool->solvables[p].repo != installed) - continue; - MAPSET(&installedm, p); - if (unneeded && !MAPTST(&userinstalled, p - installed->start)) - continue; - MAPSET(&im, p); - } - MAPSET(&installedm, SYSTEMSOLVABLE); - MAPSET(&im, SYSTEMSOLVABLE); - -#ifdef CLEANDEPSDEBUG - printf("REMOVE PASS\n"); -#endif - - for (;;) - { - if (!iq.count) - { - if (unneeded) - break; - /* supplements pass */ - for (ip = installed->start; ip < installed->end; ip++) - { - if (!MAPTST(&installedm, ip)) - continue; - s = pool->solvables + ip; - if (!s->supplements) - continue; - if (!MAPTST(&im, ip)) - continue; - if (MAPTST(&userinstalled, ip - installed->start)) - continue; - supp = s->repo->idarraydata + s->supplements; - while ((sup = *supp++) != 0) - if (dep_possible(solv, sup, &im)) - break; - if (!sup) - { - supp = s->repo->idarraydata + s->supplements; - while ((sup = *supp++) != 0) - if (dep_possible(solv, sup, &installedm) || (xsuppq.count && queue_contains(&xsuppq, sup))) - { - /* no longer supplemented, also erase */ - int iqcount = iq.count; - /* pin packages, see comment above dep_pkgcheck */ - dep_pkgcheck(solv, sup, &im, &iq); - for (i = iqcount; i < iq.count; i++) - { - Id pqp = iq.elements[i]; - if (pool->solvables[pqp].repo == installed) - MAPSET(&userinstalled, pqp - installed->start); - } - queue_truncate(&iq, iqcount); -#ifdef CLEANDEPSDEBUG - printf("%s supplemented [%s]\n", pool_solvid2str(pool, ip), pool_dep2str(pool, sup)); -#endif - queue_push(&iq, ip); - } - } - } - if (!iq.count) - break; /* no supplementing package found, we're done */ - } - ip = queue_shift(&iq); - s = pool->solvables + ip; - if (!MAPTST(&im, ip)) - continue; - if (!MAPTST(&installedm, ip)) - continue; - if (s->repo == installed && MAPTST(&userinstalled, ip - installed->start)) - continue; - MAPCLR(&im, ip); -#ifdef CLEANDEPSDEBUG - printf("removing %s\n", pool_solvable2str(pool, s)); -#endif - if (s->requires) - { - reqp = s->repo->idarraydata + s->requires; - while ((req = *reqp++) != 0) - { - if (req == SOLVABLE_PREREQMARKER) - continue; -#ifdef ENABLE_COMPLEX_DEPS - if (pool_is_complex_dep(pool, req)) - { - complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq); - continue; - } -#endif - FOR_PROVIDES(p, pp, req) - { - if (p != SYSTEMSOLVABLE && MAPTST(&im, p)) - { -#ifdef CLEANDEPSDEBUG - printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); -#endif - queue_push(&iq, p); - } - } - } - } - if (s->recommends) - { - reqp = s->repo->idarraydata + s->recommends; - while ((req = *reqp++) != 0) - { -#ifdef ENABLE_COMPLEX_DEPS - if (pool_is_complex_dep(pool, req)) - { - complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq); - continue; - } -#endif - FOR_PROVIDES(p, pp, req) - { - if (p != SYSTEMSOLVABLE && MAPTST(&im, p)) - { -#ifdef CLEANDEPSDEBUG - printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); -#endif - queue_push(&iq, p); - } - } - } - } - } - - /* turn userinstalled into remove set for pruning */ - map_empty(&userinstalled); - for (rid = solv->jobrules; rid < solv->jobrules_end; rid++) - { - r = solv->rules + rid; - if (r->p >= 0 || r->d != 0 || r->w2 != 0) - continue; /* disabled or not erase */ - p = -r->p; - MAPCLR(&im, p); - if (pool->solvables[p].repo == installed) - MAPSET(&userinstalled, p - installed->start); - } - if (!unneeded && solv->cleandeps_updatepkgs) - { - for (i = 0; i < solv->cleandeps_updatepkgs->count; i++) - { - p = solv->cleandeps_updatepkgs->elements[i]; - if (pool->solvables[p].repo == installed) - MAPSET(&userinstalled, p - installed->start); - } - } - MAPSET(&im, SYSTEMSOLVABLE); /* in case we cleared it above */ - for (p = installed->start; p < installed->end; p++) - if (MAPTST(&im, p)) - queue_push(&iq, p); - for (rid = solv->jobrules; rid < solv->jobrules_end; rid++) - { - r = solv->rules + rid; - if (r->d < 0) - continue; - FOR_RULELITERALS(p, jp, r) - if (p > 0) - queue_push(&iq, p); - } - /* also put directly addressed packages on the install queue - * so we can mark patterns as installed */ - for (i = 0; i < job->count; i += 2) - { - how = job->elements[i]; - if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED) - { - what = job->elements[i + 1]; - select = how & SOLVER_SELECTMASK; - if (select == SOLVER_SOLVABLE && pool->solvables[what].repo != installed) - queue_push(&iq, what); - } - } - -#ifdef CLEANDEPSDEBUG - printf("ADDBACK PASS\n"); -#endif - for (;;) - { - if (!iq.count) - { - /* supplements pass */ - for (ip = installed->start; ip < installed->end; ip++) - { - if (!MAPTST(&installedm, ip)) - continue; - if (MAPTST(&userinstalled, ip - installed->start)) - continue; - s = pool->solvables + ip; - if (!s->supplements) - continue; - if (MAPTST(&im, ip)) - continue; - supp = s->repo->idarraydata + s->supplements; - while ((sup = *supp++) != 0) - if (dep_possible(solv, sup, &im)) - break; - if (sup) - { -#ifdef CLEANDEPSDEBUG - printf("%s supplemented\n", pool_solvid2str(pool, ip)); -#endif - MAPSET(&im, ip); - queue_push(&iq, ip); - } - } - if (!iq.count) - break; - } - ip = queue_shift(&iq); - s = pool->solvables + ip; -#ifdef CLEANDEPSDEBUG - printf("adding back %s\n", pool_solvable2str(pool, s)); -#endif - if (s->repo == installed && pool->implicitobsoleteusescolors) - { - Id a, bestarch = 0; - FOR_PROVIDES(p, pp, s->name) - { - Solvable *ps = pool->solvables + p; - if (ps->name != s->name || ps->repo == installed) - continue; - a = ps->arch; - a = (a <= pool->lastarch) ? pool->id2arch[a] : 0; - if (a && a != 1 && (!bestarch || a < bestarch)) - bestarch = a; - } - if (bestarch && (s->arch > pool->lastarch || pool->id2arch[s->arch] != bestarch)) - { - FOR_PROVIDES(p, pp, s->name) - { - Solvable *ps = pool->solvables + p; - if (ps->repo == installed && ps->name == s->name && ps->evr == s->evr && ps->arch != s->arch && ps->arch < pool->lastarch && pool->id2arch[ps->arch] == bestarch) - if (!MAPTST(&im, p)) - { -#ifdef CLEANDEPSDEBUG - printf("%s lockstep %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); -#endif - MAPSET(&im, p); - queue_push(&iq, p); - } - } - } - } - if (s->requires) - { - reqp = s->repo->idarraydata + s->requires; - while ((req = *reqp++) != 0) - { -#ifdef ENABLE_COMPLEX_DEPS - if (pool_is_complex_dep(pool, req)) - { - complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled); - continue; - } -#endif - FOR_PROVIDES(p, pp, req) - if (p == ip) - break; - if (p) - continue; - FOR_PROVIDES(p, pp, req) - { - if (MAPTST(&im, p)) - continue; - if (MAPTST(&installedm, p)) - { - if (p == ip) - continue; - if (MAPTST(&userinstalled, p - installed->start)) - continue; -#ifdef CLEANDEPSDEBUG - printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); -#endif - MAPSET(&im, p); - queue_push(&iq, p); - } - } - } - } - if (s->recommends) - { - reqp = s->repo->idarraydata + s->recommends; - while ((req = *reqp++) != 0) - { -#ifdef ENABLE_COMPLEX_DEPS - if (pool_is_complex_dep(pool, req)) - { - complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled); - continue; - } -#endif - FOR_PROVIDES(p, pp, req) - if (p == ip) - break; - if (p) - continue; - FOR_PROVIDES(p, pp, req) - { - if (MAPTST(&im, p)) - continue; - if (MAPTST(&installedm, p)) - { - if (p == ip) - continue; - if (MAPTST(&userinstalled, p - installed->start)) - continue; -#ifdef CLEANDEPSDEBUG - printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p)); -#endif - MAPSET(&im, p); - queue_push(&iq, p); - } - } - } - } - } - - queue_free(&iq); - /* make sure the updatepkgs and mistakes are not in the cleandeps map */ - if (solv->cleandeps_updatepkgs) - for (i = 0; i < solv->cleandeps_updatepkgs->count; i++) - MAPSET(&im, solv->cleandeps_updatepkgs->elements[i]); - if (solv->cleandeps_mistakes) - for (i = 0; i < solv->cleandeps_mistakes->count; i++) - MAPSET(&im, solv->cleandeps_mistakes->elements[i]); - /* also remove original iq packages */ - for (i = 0; i < iqcopy.count; i++) - MAPSET(&im, iqcopy.elements[i]); - queue_free(&iqcopy); - for (p = installed->start; p < installed->end; p++) - { - if (pool->solvables[p].repo != installed) - continue; - if (!MAPTST(&im, p)) - MAPSET(cleandepsmap, p - installed->start); - } - map_free(&im); - map_free(&installedm); - map_free(&userinstalled); - queue_free(&xsuppq); -#ifdef CLEANDEPSDEBUG - printf("=== final cleandeps map:\n"); - for (p = installed->start; p < installed->end; p++) - if (MAPTST(cleandepsmap, p - installed->start)) - printf(" - %s\n", pool_solvid2str(pool, p)); -#endif -} - - -struct trj_data { - Queue *edges; - Id *low; - Id idx; - Id nstack; - Id firstidx; -}; - -/* Tarjan's SCC algorithm, slightly modifed */ -static void -trj_visit(struct trj_data *trj, Id node) -{ - Id *low = trj->low; - Queue *edges = trj->edges; - Id nnode, myidx, stackstart; - int i; - - low[node] = myidx = trj->idx++; - low[(stackstart = trj->nstack++)] = node; - for (i = edges->elements[node]; (nnode = edges->elements[i]) != 0; i++) - { - Id l = low[nnode]; - if (!l) - { - if (!edges->elements[edges->elements[nnode]]) - { - trj->idx++; - low[nnode] = -1; - continue; - } - trj_visit(trj, nnode); - l = low[nnode]; - } - if (l < 0) - continue; - if (l < trj->firstidx) - { - int k; - for (k = l; low[low[k]] == l; k++) - low[low[k]] = -1; - } - else if (l < low[node]) - low[node] = l; - } - if (low[node] == myidx) - { - if (myidx != trj->firstidx) - myidx = -1; - for (i = stackstart; i < trj->nstack; i++) - low[low[i]] = myidx; - trj->nstack = stackstart; - } -} - -#ifdef ENABLE_COMPLEX_DEPS -static void -complex_unneeded(Pool *pool, Id ip, Id req, Queue *edges, Map *cleandepsmap, Queue *unneededq) -{ - int i, j; - Queue dq; - Id p; - - queue_init(&dq); - i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND); - if (i == 0 || i == 1) - { - queue_free(&dq); - return; - } - for (i = 0; i < dq.count; i++) - { - for (; (p = dq.elements[i]) != 0; i++) - { - if (p < 0) - { - if (pool->solvables[-p].repo != pool->installed) - break; - continue; - } - if (p == ip || pool->solvables[p].repo != pool->installed || !MAPTST(cleandepsmap, p - pool->installed->start)) - continue; - for (j = 0; j < unneededq->count; j++) - if (p == unneededq->elements[j]) - { - if (edges->elements[edges->count - 1] != j + 1) - queue_push(edges, j + 1); - break; - } - } - while (dq.elements[i]) - i++; - } - queue_free(&dq); -} -#endif - -void -solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered) -{ - Repo *installed = solv->installed; - int i; - Map cleandepsmap; - - queue_empty(unneededq); - if (!installed || installed->end == installed->start) - return; - - map_init(&cleandepsmap, installed->end - installed->start); - solver_createcleandepsmap(solv, &cleandepsmap, 1); - for (i = installed->start; i < installed->end; i++) - if (MAPTST(&cleandepsmap, i - installed->start)) - queue_push(unneededq, i); - - if (filtered && unneededq->count > 1) - { - Pool *pool = solv->pool; - Queue edges; - Id *nrequires; - Map installedm; - int j, pass, count = unneededq->count; - Id *low; - - map_init(&installedm, pool->nsolvables); - for (i = installed->start; i < installed->end; i++) - if (pool->solvables[i].repo == installed) - MAPSET(&installedm, i); - - nrequires = solv_calloc(count, sizeof(Id)); - queue_init(&edges); - queue_prealloc(&edges, count * 4 + 10); /* pre-size */ - - /* - * Go through the solvables in the nodes queue and create edges for - * all requires/recommends/supplements between the nodes. - * The edges are stored in the edges queue, we add 1 to the node - * index so that nodes in the edges queue are != 0 and we can - * terminate the edge list with 0. - * Thus for node element 5, the edges are stored starting at - * edges.elements[6] and are 0-terminated. - */ - /* leave first element zero to make things easier */ - /* also add trailing zero */ - queue_insertn(&edges, 0, 1 + count + 1, 0); - - /* first requires and recommends */ - for (i = 0; i < count; i++) - { - Solvable *s = pool->solvables + unneededq->elements[i]; - int oldcount = edges.count; - edges.elements[i + 1] = oldcount; - for (pass = 0; pass < 2; pass++) - { - unsigned int off = pass == 0 ? s->requires : s->recommends; - Id p, pp, dep, *dp; - if (off) - for (dp = s->repo->idarraydata + off; (dep = *dp) != 0; dp++) - { -#ifdef ENABLE_COMPLEX_DEPS - if (pool_is_complex_dep(pool, dep)) - { - complex_unneeded(pool, s - pool->solvables, dep, &edges, &cleandepsmap, unneededq); - continue; - } -#endif - FOR_PROVIDES(p, pp, dep) - { - Solvable *sp = pool->solvables + p; - if (s == sp || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start)) - continue; - for (j = 0; j < count; j++) - if (p == unneededq->elements[j]) - { - if (edges.elements[edges.count - 1] != j + 1) - queue_push(&edges, j + 1); - } - } - } - if (pass == 0) - nrequires[i] = edges.count - oldcount; - } - queue_push(&edges, 0); - } -#if 0 - printf("requires + recommends\n"); - for (i = 0; i < count; i++) - { - int j; - printf(" %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]); - for (j = edges.elements[i + 1]; edges.elements[j]; j++) - printf(" - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1])); - } -#endif - - /* then add supplements */ - for (i = 0; i < count; i++) - { - Solvable *s = pool->solvables + unneededq->elements[i]; - if (s->supplements) - { - Id *dp; - int k; - for (dp = s->repo->idarraydata + s->supplements; *dp; dp++) - if (dep_possible(solv, *dp, &installedm)) - { - Queue iq; - Id iqbuf[16]; - queue_init_buffer(&iq, iqbuf, sizeof(iqbuf)/sizeof(*iqbuf)); - dep_pkgcheck(solv, *dp, 0, &iq); - for (k = 0; k < iq.count; k++) - { - Id p = iq.elements[k]; - Solvable *sp = pool->solvables + p; - if (p == unneededq->elements[i] || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start)) - continue; - for (j = 0; j < count; j++) - if (p == unneededq->elements[j]) - break; - /* now add edge from j + 1 to i + 1 */ - queue_insert(&edges, edges.elements[j + 1] + nrequires[j], i + 1); - /* addapt following edge pointers */ - for (j = j + 2; j < count + 1; j++) - edges.elements[j]++; - } - queue_free(&iq); - } - } - } -#if 0 - /* print result */ - printf("+ supplements\n"); - for (i = 0; i < count; i++) - { - int j; - printf(" %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]); - for (j = edges.elements[i + 1]; edges.elements[j]; j++) - printf(" - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1])); - } -#endif - map_free(&installedm); - - /* now run SCC algo two times, first with requires+recommends+supplements, - * then again without the requires. We run it the second time to get rid - * of packages that got dragged in via recommends/supplements */ - /* - * low will contain the result of the SCC search. - * it must be of at least size 2 * (count + 1) and - * must be zero initialized. - * The layout is: - * 0 low low ... low stack stack ...stack 0 - * count count - */ - low = solv_calloc(count + 1, 2 * sizeof(Id)); - for (pass = 0; pass < 2; pass++) - { - struct trj_data trj; - if (pass) - { - memset(low, 0, (count + 1) * (2 * sizeof(Id))); - for (i = 0; i < count; i++) - { - edges.elements[i + 1] += nrequires[i]; - if (!unneededq->elements[i]) - low[i + 1] = -1; /* ignore this node */ - } - } - trj.edges = &edges; - trj.low = low; - trj.idx = count + 1; /* stack starts here */ - for (i = 1; i <= count; i++) - { - if (low[i]) - continue; - if (edges.elements[edges.elements[i]]) - { - trj.firstidx = trj.nstack = trj.idx; - trj_visit(&trj, i); - } - else - { - Id myidx = trj.idx++; - low[i] = myidx; - low[myidx] = i; - } - } - /* prune packages */ - for (i = 0; i < count; i++) - if (low[i + 1] <= 0) - unneededq->elements[i] = 0; - } - solv_free(low); - solv_free(nrequires); - queue_free(&edges); - - /* finally remove all pruned entries from unneededq */ - for (i = j = 0; i < count; i++) - if (unneededq->elements[i]) - unneededq->elements[j++] = unneededq->elements[i]; - queue_truncate(unneededq, j); - } - map_free(&cleandepsmap); -} - - void solver_breakorphans(Solver *solv) { diff --git a/src/solver.c b/src/solver.c index 5d1052d..037b33d 100644 --- a/src/solver.c +++ b/src/solver.c @@ -31,242 +31,6 @@ #define RULES_BLOCK 63 -/******************************************************************** - * - * dependency check helpers - * - */ - -/*------------------------------------------------------------------- - * handle split provides - * - * a splitprovides dep looks like - * namespace:splitprovides(pkg REL_WITH path) - * and is only true if pkg is installed and contains the specified path. - * we also make sure that pkg is selected for an update, otherwise the - * update would always be forced onto the user. - * Map m is the map used when called from dep_possible. - */ - -static int -solver_is_updating(Solver *solv, Id p) -{ - /* check if the update rule is true */ - Pool *pool = solv->pool; - Rule *r; - Id l, pp; - if (solv->decisionmap[p] >= 0) - return 0; /* old package stayed */ - r = solv->rules + solv->updaterules + (p - solv->installed->start); - FOR_RULELITERALS(l, pp, r) - if (l > 0 && l != p && solv->decisionmap[l] > 0) - return 1; - return 0; -} - -int -solver_splitprovides(Solver *solv, Id dep, Map *m) -{ - Pool *pool = solv->pool; - Id p, pp; - Reldep *rd; - Solvable *s; - - if (!solv->dosplitprovides || !solv->installed) - return 0; - if (!ISRELDEP(dep)) - return 0; - rd = GETRELDEP(pool, dep); - if (rd->flags != REL_WITH) - return 0; - /* - * things are a bit tricky here if pool->addedprovides == 1, because most split-provides are in - * a non-standard location. If we simply call pool_whatprovides, we'll drag in the complete - * file list. Instead we rely on pool_addfileprovides ignoring the addfileprovidesfiltered flag - * for installed packages and check the lazywhatprovidesq (ignoring the REL_WITH part, but - * we filter the package name further down anyway). - */ - if (pool->addedfileprovides == 1 && !ISRELDEP(rd->evr) && !pool->whatprovides[rd->evr]) - pp = pool_searchlazywhatprovidesq(pool, rd->evr); - else - pp = pool_whatprovides(pool, dep); - while ((p = pool->whatprovidesdata[pp++]) != 0) - { - /* here we have packages that provide the correct name and contain the path, - * now do extra filtering */ - s = pool->solvables + p; - if (s->repo != solv->installed || s->name != rd->name) - continue; - /* check if the package is updated. if m is set, we're called from dep_possible */ - if (m || solver_is_updating(solv, p)) - return 1; - } - return 0; -} - -int -solver_dep_fulfilled_cplx(Solver *solv, Reldep *rd) -{ - Pool *pool = solv->pool; - if (rd->flags == REL_COND) - { - if (ISRELDEP(rd->evr)) - { - Reldep *rd2 = GETRELDEP(pool, rd->evr); - if (rd2->flags == REL_ELSE) - { - if (solver_dep_fulfilled(solv, rd2->name)) - return solver_dep_fulfilled(solv, rd->name); - return solver_dep_fulfilled(solv, rd2->evr); - } - } - if (solver_dep_fulfilled(solv, rd->name)) - return 1; - return !solver_dep_fulfilled(solv, rd->evr); - } - if (rd->flags == REL_UNLESS) - { - if (ISRELDEP(rd->evr)) - { - Reldep *rd2 = GETRELDEP(pool, rd->evr); - if (rd2->flags == REL_ELSE) - { - if (!solver_dep_fulfilled(solv, rd2->name)) - return solver_dep_fulfilled(solv, rd->name); - return solver_dep_fulfilled(solv, rd2->evr); - } - } - if (!solver_dep_fulfilled(solv, rd->name)) - return 0; - return !solver_dep_fulfilled(solv, rd->evr); - } - if (rd->flags == REL_AND) - { - if (!solver_dep_fulfilled(solv, rd->name)) - return 0; - return solver_dep_fulfilled(solv, rd->evr); - } - if (rd->flags == REL_OR) - { - if (solver_dep_fulfilled(solv, rd->name)) - return 1; - return solver_dep_fulfilled(solv, rd->evr); - } - return 0; -} - - -/* mirrors solver_dep_fulfilled, but returns 2 if a new package - * was involved */ -static int -solver_dep_fulfilled_alreadyinstalled(Solver *solv, Id dep) -{ - Pool *pool = solv->pool; - Id p, pp; - int r; - - if (ISRELDEP(dep)) - { - Reldep *rd = GETRELDEP(pool, dep); - if (rd->flags == REL_COND) - { - int r1, r2; - if (ISRELDEP(rd->evr)) - { - Reldep *rd2 = GETRELDEP(pool, rd->evr); - if (rd2->flags == REL_ELSE) - { - r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name); - if (r1) - { - r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); - return r2 && r1 == 2 ? 2 : r2; - } - return solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr); - } - } - r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); - r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr); - if (!r1 && !r2) - return 0; - return r1 == 2 ? 2 : 1; - } - if (rd->flags == REL_UNLESS) - { - int r1, r2; - if (ISRELDEP(rd->evr)) - { - Reldep *rd2 = GETRELDEP(pool, rd->evr); - if (rd2->flags == REL_ELSE) - { - r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name); - if (r1) - { - r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr); - return r2 && r1 == 2 ? 2 : r2; - } - return solver_dep_fulfilled_alreadyinstalled(solv, rd->name); - } - } - /* A AND NOT(B) */ - r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); - r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr); - if (!r1 || !r2) - return 0; - return r1 == 2 ? 2 : 1; - } - if (rd->flags == REL_AND) - { - int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); - if (!r1) - return 0; - r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr); - if (!r2) - return 0; - return r1 == 2 || r2 == 2 ? 2 : 1; - } - if (rd->flags == REL_OR) - { - int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); - r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr); - if (!r1 && !r2) - return 0; - return r1 == 2 || r2 == 2 ? 2 : 1; - } - if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES) - return solver_splitprovides(solv, rd->evr, 0); - if (rd->flags == REL_NAMESPACE && solv->installsuppdepq) - { - Queue *q = solv->installsuppdepq; - int i; - for (i = 0; i < q->count; i++) - if (q->elements[i] == dep || q->elements[i] == rd->name) - return 2; - } - } - r = 0; - FOR_PROVIDES(p, pp, dep) - if (solv->decisionmap[p] > 0) - { - Solvable *s = pool->solvables + p; - if (s->repo && s->repo != solv->installed) - return 2; - r = 1; - } - return r; -} - -static int -solver_is_supplementing_alreadyinstalled(Solver *solv, Solvable *s) -{ - Id sup, *supp; - supp = s->repo->idarraydata + s->supplements; - while ((sup = *supp++) != 0) - if (solver_dep_fulfilled_alreadyinstalled(solv, sup) == 2) - return 1; - return 0; -} - static Id autouninstall(Solver *solv, Id *problem) { @@ -495,6 +259,10 @@ makeruledecisions(Solver *solv) continue; } + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "ANALYZE UNSOLVABLE ASSERTION ----------------------\n"); + IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE) + solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + ri); + /* * find the decision which is the "opposite" of the rule */ @@ -540,6 +308,8 @@ makeruledecisions(Solver *solv) } assert(solv->decisionq_why.elements[i] > 0); + IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE) + solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + solv->decisionq_why.elements[i]); /* * conflict with a pkg rule ? @@ -2002,56 +1772,6 @@ resolve_jobrules(Solver *solv, int level, int disablerules, Queue *dq) return level; } -static int -cleandeps_check_mistakes(Solver *solv) -{ - Pool *pool = solv->pool; - Rule *r; - Id p, pp; - int i; - int mademistake = 0; - - if (!solv->cleandepsmap.size) - return 0; - /* check for mistakes */ - for (i = solv->installed->start; i < solv->installed->end; i++) - { - if (!MAPTST(&solv->cleandepsmap, i - solv->installed->start)) - continue; - r = solv->rules + solv->featurerules + (i - solv->installed->start); - /* a mistake is when the featurerule is true but the updaterule is false */ - if (!r->p) - continue; - FOR_RULELITERALS(p, pp, r) - if (p > 0 && solv->decisionmap[p] > 0) - break; - if (!p) - continue; /* feature rule is not true */ - r = solv->rules + solv->updaterules + (i - solv->installed->start); - if (!r->p) - continue; - FOR_RULELITERALS(p, pp, r) - if (p > 0 && solv->decisionmap[p] > 0) - break; - if (p) - continue; /* update rule is true */ - POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps mistake: "); - solver_printruleclass(solv, SOLV_DEBUG_SOLVER, r); - POOL_DEBUG(SOLV_DEBUG_SOLVER, "feature rule: "); - solver_printruleclass(solv, SOLV_DEBUG_SOLVER, solv->rules + solv->featurerules + (i - solv->installed->start)); - if (!solv->cleandeps_mistakes) - { - solv->cleandeps_mistakes = solv_calloc(1, sizeof(Queue)); - queue_init(solv->cleandeps_mistakes); - } - queue_push(solv->cleandeps_mistakes, i); - MAPCLR(&solv->cleandepsmap, i - solv->installed->start); - solver_reenablepolicyrules_cleandeps(solv, i); - mademistake = 1; - } - return mademistake; -} - static void prune_to_update_targets(Solver *solv, Id *cp, Queue *q) { @@ -2398,7 +2118,7 @@ add_complex_recommends(Solver *solv, Id rec, Queue *dq, Map *dqmap) { if (solv->decisionmap[p] < 0) continue; - if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start)))) + if (solv->process_orphans && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start)))) continue; } queue_push(dq, p); @@ -2553,7 +2273,7 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i } else if (solv->decisionmap[p] == 0) { - if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start)))) + if (solv->process_orphans && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start)))) continue; queue_pushunique(dq, p); } @@ -2570,7 +2290,7 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i continue; if (!solver_is_supplementing(solv, s)) continue; - if (solv->dupmap_all && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start)))) + if (solv->process_orphans && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start)))) continue; if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, i)) continue; /* disfavored supplements, do not install */ @@ -2794,6 +2514,35 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i return level; } +static int +resolve_cleandeps(Solver *solv, int level, int disablerules, int *rerunp) +{ + Pool *pool = solv->pool; + Repo *installed = solv->installed; + int olevel; + Id p; + Solvable *s; + + if (!installed || !solv->cleandepsmap.size) + return level; + POOL_DEBUG(SOLV_DEBUG_SOLVER, "deciding cleandeps packages\n"); + for (p = installed->start; p < installed->end; p++) + { + s = pool->solvables + p; + if (s->repo != installed) + continue; + if (solv->decisionmap[p] != 0 || !MAPTST(&solv->cleandepsmap, p - installed->start)) + continue; + POOL_DEBUG(SOLV_DEBUG_POLICY, "cleandeps erasing %s\n", pool_solvid2str(pool, p)); + olevel = level; + level = setpropagatelearn(solv, level, -p, 0, 0, SOLVER_REASON_CLEANDEPS_ERASE); + if (level < olevel) + break; + } + if (p < installed->end) + *rerunp = 1; + return level; +} static int resolve_orphaned(Solver *solv, int level, int disablerules, Queue *dq, int *rerunp) @@ -2999,24 +2748,12 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) /* decide leftover cleandeps packages */ if (solv->cleandepsmap.size && solv->installed) - { - for (p = solv->installed->start; p < solv->installed->end; p++) - { - s = pool->solvables + p; - if (s->repo != solv->installed) - continue; - if (solv->decisionmap[p] == 0 && MAPTST(&solv->cleandepsmap, p - solv->installed->start)) - { - POOL_DEBUG(SOLV_DEBUG_POLICY, "cleandeps erasing %s\n", pool_solvid2str(pool, p)); - olevel = level; - level = setpropagatelearn(solv, level, -p, 0, 0, SOLVER_REASON_CLEANDEPS_ERASE); - if (level < olevel) - break; - } - } - if (p < solv->installed->end) - continue; - } + { + int rerun = 0; + level = resolve_cleandeps(solv, level, disablerules, &rerun); + if (rerun) + continue; + } /* at this point we have a consistent system. now do the extras... */ @@ -3056,7 +2793,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) continue; /* back to main loop */ } - if (solv->installed && solv->cleandepsmap.size && cleandeps_check_mistakes(solv)) + if (solv->installed && solv->cleandepsmap.size && solver_check_cleandeps_mistakes(solv)) { solver_reset(solv); level = 0; /* restart from scratch */ @@ -3366,7 +3103,7 @@ solver_addjobrule(Solver *solv, Id p, Id p2, Id d, Id job, int weak) } static inline void -add_cleandeps_package(Solver *solv, Id p) +add_cleandeps_updatepkg(Solver *solv, Id p) { if (!solv->cleandeps_updatepkgs) { @@ -3405,7 +3142,7 @@ add_update_target(Solver *solv, Id p, Id how) MAPSET(&solv->bestupdatemap, pi - installed->start); } if (how & SOLVER_CLEANDEPS) - add_cleandeps_package(solv, pi); + add_cleandeps_updatepkg(solv, pi); queue_push2(solv->update_targets, pi, p); /* check if it's ok to keep the installed package */ if (s->evr == si->evr && solvable_identical(s, si)) @@ -3434,7 +3171,7 @@ add_update_target(Solver *solv, Id p, Id how) MAPSET(&solv->bestupdatemap, pi - installed->start); } if (how & SOLVER_CLEANDEPS) - add_cleandeps_package(solv, pi); + add_cleandeps_updatepkg(solv, pi); queue_push2(solv->update_targets, pi, p); } } @@ -3677,7 +3414,7 @@ solver_solve(Solver *solv, Queue *job) queue_insertn(&solv->job, 0, pool->pooljobs.count, pool->pooljobs.elements); job = &solv->job; - /* free old stuff in jase we re-run a solver */ + /* free old stuff in case we re-run a solver */ queuep_free(&solv->update_targets); queuep_free(&solv->cleandeps_updatepkgs); queue_empty(&solv->ruleassertions); @@ -3693,9 +3430,9 @@ solver_solve(Solver *solv, Queue *job) map_zerosize(&solv->bestupdatemap); solv->fixmap_all = 0; map_zerosize(&solv->fixmap); - solv->dupmap_all = 0; map_zerosize(&solv->dupmap); map_zerosize(&solv->dupinvolvedmap); + solv->process_orphans = 0; solv->droporphanedmap_all = 0; map_zerosize(&solv->droporphanedmap); solv->allowuninstall_all = 0; @@ -3794,7 +3531,7 @@ solver_solve(Solver *solv, Queue *job) if (how & SOLVER_CLEANDEPS) { FOR_REPO_SOLVABLES(installed, p, s) - add_cleandeps_package(solv, p); + add_cleandeps_updatepkg(solv, p); } } else if (select == SOLVER_SOLVABLE_REPO) @@ -3810,7 +3547,7 @@ solver_solve(Solver *solv, Queue *job) if (how & SOLVER_CLEANDEPS) { FOR_REPO_SOLVABLES(installed, p, s) - add_cleandeps_package(solv, p); + add_cleandeps_updatepkg(solv, p); } break; } @@ -3840,7 +3577,7 @@ solver_solve(Solver *solv, Queue *job) MAPSET(&solv->bestupdatemap, p - installed->start); } if (how & SOLVER_CLEANDEPS) - add_cleandeps_package(solv, p); + add_cleandeps_updatepkg(solv, p); targeted = 0; } if (!targeted || solv->noautotarget) @@ -3916,17 +3653,9 @@ solver_solve(Solver *solv, Queue *job) } break; case SOLVER_DISTUPGRADE: + needduprules = 1; if (select == SOLVER_SOLVABLE_ALL) - { - solv->dupmap_all = 1; - solv->updatemap_all = 1; - if (how & SOLVER_FORCEBEST) - solv->bestupdatemap_all = 1; - } - if ((how & SOLVER_TARGETED) != 0) - needduprules = 1; - if (!solv->dupmap_all || solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size || solv->keep_orphans) - needduprules = 1; + solv->process_orphans = 1; break; default: break; @@ -4049,7 +3778,7 @@ solver_solve(Solver *solv, Queue *job) continue; } /* it's also orphaned if the feature rule consists just of the installed package */ - if (!solv->dupmap_all && sr->p == i && !sr->d && !sr->w2) + if (!solv->process_orphans && sr->p == i && !sr->d && !sr->w2) queue_push(&solv->orphaned, i); if (!solver_rulecmp(solv, r, sr)) @@ -4307,7 +4036,7 @@ solver_solve(Solver *solv, Queue *job) else solv->infarchrules = solv->infarchrules_end = solv->nrules; - if (needduprules) + if (solv->dupinvolvedmap_all || solv->dupinvolvedmap.size) solver_addduprules(solv, &addedmap); else solv->duprules = solv->duprules_end = solv->nrules; @@ -4398,7 +4127,7 @@ solver_solve(Solver *solv, Queue *job) solver_disablepolicyrules(solv); /* break orphans if requested */ - if (solv->dupmap_all && solv->orphaned.count && solv->break_orphans) + if (solv->process_orphans && solv->orphaned.count && solv->break_orphans) solver_breakorphans(solv); /* @@ -4435,6 +4164,35 @@ void solver_get_orphaned(Solver *solv, Queue *orphanedq) queue_init_clone(orphanedq, &solv->orphaned); } +void solver_get_cleandeps(Solver *solv, Queue *cleandepsq) +{ + Pool *pool = solv->pool; + Repo *installed = solv->installed; + Solvable *s; + Rule *r; + Id p, pp, pr; + + queue_empty(cleandepsq); + if (!installed || !solv->cleandepsmap.size) + return; + FOR_REPO_SOLVABLES(installed, p, s) + { + if (!MAPTST(&solv->cleandepsmap, p - installed->start) || solv->decisionmap[p] >= 0) + continue; + /* now check the update rule */ + r = solv->rules + solv->updaterules + (p - solv->installed->start); + if (r->p) + { + FOR_RULELITERALS(pr, pp, r) + if (solv->decisionmap[pr] > 0) + break; + if (pr) + continue; + } + queue_push(cleandepsq, p); + } +} + void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *suggestionsq, int noselected) { Pool *pool = solv->pool; @@ -4805,7 +4563,7 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) for (i = 1; i < pool->nsolvables; i++) { Id *recp, rec, pp2, p2; - if (solv->decisionmap[i] < 0 || solv->decisionmap[i] >= level) + if (solv->decisionmap[i] <= 0 || solv->decisionmap[i] >= level) continue; s = pool->solvables + i; if (!s->recommends) diff --git a/src/solver.h b/src/solver.h index 15fdf3f..1b85fb3 100644 --- a/src/solver.h +++ b/src/solver.h @@ -167,9 +167,10 @@ struct _Solver { int strongrecommends; /* true: create weak rules for recommends */ int install_also_updates; /* true: do not prune install job rules to installed packages */ - Map dupmap; /* dup these packages*/ - int dupmap_all; /* dup all packages */ + int process_orphans; /* true: do special orphan processing */ + Map dupmap; /* dup to those packages */ Map dupinvolvedmap; /* packages involved in dup process */ + int dupinvolvedmap_all; /* all packages are involved */ int dup_allowdowngrade; /* dup mode: allow to downgrade installed solvable */ int dup_allownamechange; /* dup mode: allow to change name of installed solvable */ int dup_allowarchchange; /* dup mode: allow to change architecture of installed solvables */ @@ -341,6 +342,7 @@ extern void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Qu extern void solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered); extern void solver_get_userinstalled(Solver *solv, Queue *q, int flags); extern void pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags); +extern void solver_get_cleandeps(Solver *solv, Queue *cleandepsq); extern int solver_describe_decision(Solver *solv, Id p, Id *infop); extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq); diff --git a/src/solver_private.h b/src/solver_private.h index a234223..e92617d 100644 --- a/src/solver_private.h +++ b/src/solver_private.h @@ -17,7 +17,32 @@ extern void solver_run_sat(Solver *solv, int disablerules, int doweak); extern void solver_reset(Solver *solv); extern int solver_splitprovides(Solver *solv, Id dep, Map *m); +extern int solver_dep_possible_slow(Solver *solv, Id dep, Map *m); extern int solver_dep_fulfilled_cplx(Solver *solv, Reldep *rd); +extern int solver_is_supplementing_alreadyinstalled(Solver *solv, Solvable *s); +extern void solver_intersect_obsoleted(Solver *solv, Id p, Queue *q, int qstart, Map *m); + +extern void solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded); +extern int solver_check_cleandeps_mistakes(Solver *solv); + + +#define ISSIMPLEDEP(pool, dep) (!ISRELDEP(dep) || GETRELDEP(pool, dep)->flags < 8) + +static inline int +solver_dep_possible(Solver *solv, Id dep, Map *m) +{ + Pool *pool = solv->pool; + Id p, pp; + + if (!ISSIMPLEDEP(pool, dep)) + return solver_dep_possible_slow(solv, dep, m); + FOR_PROVIDES(p, pp, dep) + { + if (MAPTST(m, p)) + return 1; + } + return 0; +} static inline int solver_dep_fulfilled(Solver *solv, Id dep) diff --git a/src/solver_util.c b/src/solver_util.c new file mode 100644 index 0000000..bd5d4b4 --- /dev/null +++ b/src/solver_util.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2017, SUSE Inc. + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +/* + * solver_util.c + * + * Dependency solver helper functions + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "solver.h" +#include "solver_private.h" +#include "bitmap.h" +#include "pool.h" +#include "poolarch.h" +#include "util.h" + + +/*------------------------------------------------------------------- + * check if a installed package p is being updated + */ +static int +solver_is_updating(Solver *solv, Id p) +{ + /* check if the update rule is true */ + Pool *pool = solv->pool; + Rule *r; + Id l, pp; + if (solv->decisionmap[p] >= 0) + return 0; /* old package stayed */ + r = solv->rules + solv->updaterules + (p - solv->installed->start); + FOR_RULELITERALS(l, pp, r) + if (l > 0 && l != p && solv->decisionmap[l] > 0) + return 1; + return 0; +} + +/*------------------------------------------------------------------- + * handle split provides + * + * a splitprovides dep looks like + * namespace:splitprovides(pkg REL_WITH path) + * and is only true if pkg is installed and contains the specified path. + * we also make sure that pkg is selected for an update, otherwise the + * update would always be forced onto the user. + * Map m is the map used when called from dep_possible. + */ +int +solver_splitprovides(Solver *solv, Id dep, Map *m) +{ + Pool *pool = solv->pool; + Id p, pp; + Reldep *rd; + Solvable *s; + + if (!solv->dosplitprovides || !solv->installed) + return 0; + if (!ISRELDEP(dep)) + return 0; + rd = GETRELDEP(pool, dep); + if (rd->flags != REL_WITH) + return 0; + /* + * things are a bit tricky here if pool->addedprovides == 1, because most split-provides are in + * a non-standard location. If we simply call pool_whatprovides, we'll drag in the complete + * file list. Instead we rely on pool_addfileprovides ignoring the addfileprovidesfiltered flag + * for installed packages and check the lazywhatprovidesq (ignoring the REL_WITH part, but + * we filter the package name further down anyway). + */ + if (pool->addedfileprovides == 1 && !ISRELDEP(rd->evr) && !pool->whatprovides[rd->evr]) + pp = pool_searchlazywhatprovidesq(pool, rd->evr); + else + pp = pool_whatprovides(pool, dep); + while ((p = pool->whatprovidesdata[pp++]) != 0) + { + /* here we have packages that provide the correct name and contain the path, + * now do extra filtering */ + s = pool->solvables + p; + if (s->repo != solv->installed || s->name != rd->name) + continue; + /* check if the package is updated. if m is set, we're called from dep_possible */ + if (m || solver_is_updating(solv, p)) + return 1; + } + return 0; +} + +int +solver_dep_possible_slow(Solver *solv, Id dep, Map *m) +{ + Pool *pool = solv->pool; + Id p, pp; + + if (ISRELDEP(dep)) + { + Reldep *rd = GETRELDEP(pool, dep); + if (rd->flags >= 8) + { + if (rd->flags == REL_COND || rd->flags == REL_UNLESS) + return 1; + if (rd->flags == REL_AND) + { + if (!solver_dep_possible_slow(solv, rd->name, m)) + return 0; + return solver_dep_possible_slow(solv, rd->evr, m); + } + if (rd->flags == REL_OR) + { + if (solver_dep_possible_slow(solv, rd->name, m)) + return 1; + return solver_dep_possible_slow(solv, rd->evr, m); + } + if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES) + return solver_splitprovides(solv, rd->evr, m); + } + } + FOR_PROVIDES(p, pp, dep) + { + if (MAPTST(m, p)) + return 1; + } + return 0; +} + +int +solver_dep_fulfilled_cplx(Solver *solv, Reldep *rd) +{ + Pool *pool = solv->pool; + if (rd->flags == REL_COND) + { + if (ISRELDEP(rd->evr)) + { + Reldep *rd2 = GETRELDEP(pool, rd->evr); + if (rd2->flags == REL_ELSE) + { + if (solver_dep_fulfilled(solv, rd2->name)) + return solver_dep_fulfilled(solv, rd->name); + return solver_dep_fulfilled(solv, rd2->evr); + } + } + if (solver_dep_fulfilled(solv, rd->name)) + return 1; + return !solver_dep_fulfilled(solv, rd->evr); + } + if (rd->flags == REL_UNLESS) + { + if (ISRELDEP(rd->evr)) + { + Reldep *rd2 = GETRELDEP(pool, rd->evr); + if (rd2->flags == REL_ELSE) + { + if (!solver_dep_fulfilled(solv, rd2->name)) + return solver_dep_fulfilled(solv, rd->name); + return solver_dep_fulfilled(solv, rd2->evr); + } + } + if (!solver_dep_fulfilled(solv, rd->name)) + return 0; + return !solver_dep_fulfilled(solv, rd->evr); + } + if (rd->flags == REL_AND) + { + if (!solver_dep_fulfilled(solv, rd->name)) + return 0; + return solver_dep_fulfilled(solv, rd->evr); + } + if (rd->flags == REL_OR) + { + if (solver_dep_fulfilled(solv, rd->name)) + return 1; + return solver_dep_fulfilled(solv, rd->evr); + } + return 0; +} + + +/* mirrors solver_dep_fulfilled, but returns 2 if a new package + * was involved */ +static int +solver_dep_fulfilled_alreadyinstalled(Solver *solv, Id dep) +{ + Pool *pool = solv->pool; + Id p, pp; + int r; + + if (ISRELDEP(dep)) + { + Reldep *rd = GETRELDEP(pool, dep); + if (rd->flags == REL_COND) + { + int r1, r2; + if (ISRELDEP(rd->evr)) + { + Reldep *rd2 = GETRELDEP(pool, rd->evr); + if (rd2->flags == REL_ELSE) + { + r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name); + if (r1) + { + r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); + return r2 && r1 == 2 ? 2 : r2; + } + return solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr); + } + } + r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); + r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr); + if (!r1 && !r2) + return 0; + return r1 == 2 ? 2 : 1; + } + if (rd->flags == REL_UNLESS) + { + int r1, r2; + if (ISRELDEP(rd->evr)) + { + Reldep *rd2 = GETRELDEP(pool, rd->evr); + if (rd2->flags == REL_ELSE) + { + r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name); + if (r1) + { + r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr); + return r2 && r1 == 2 ? 2 : r2; + } + return solver_dep_fulfilled_alreadyinstalled(solv, rd->name); + } + } + /* A AND NOT(B) */ + r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); + r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr); + if (!r1 || !r2) + return 0; + return r1 == 2 ? 2 : 1; + } + if (rd->flags == REL_AND) + { + int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); + if (!r1) + return 0; + r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr); + if (!r2) + return 0; + return r1 == 2 || r2 == 2 ? 2 : 1; + } + if (rd->flags == REL_OR) + { + int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name); + r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr); + if (!r1 && !r2) + return 0; + return r1 == 2 || r2 == 2 ? 2 : 1; + } + if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES) + return solver_splitprovides(solv, rd->evr, 0) ? 2 : 0; + if (rd->flags == REL_NAMESPACE && solv->installsuppdepq) + { + Queue *q = solv->installsuppdepq; + int i; + for (i = 0; i < q->count; i++) + if (q->elements[i] == dep || q->elements[i] == rd->name) + return 2; + } + } + r = 0; + FOR_PROVIDES(p, pp, dep) + if (solv->decisionmap[p] > 0) + { + Solvable *s = pool->solvables + p; + if (s->repo && s->repo != solv->installed) + return 2; + r = 1; + } + return r; +} + +int +solver_is_supplementing_alreadyinstalled(Solver *solv, Solvable *s) +{ + Id sup, *supp; + supp = s->repo->idarraydata + s->supplements; + while ((sup = *supp++) != 0) + if (solver_dep_fulfilled_alreadyinstalled(solv, sup) == 2) + return 1; + return 0; +} +/* + * add all installed packages that package p obsoletes to Queue q. + * Package p is not installed. Also, we know that if + * solv->keepexplicitobsoletes is not set, p is not in the multiversion map. + * Entries may get added multiple times. + */ +static void +solver_add_obsoleted(Solver *solv, Id p, Queue *q) +{ + Pool *pool = solv->pool; + Repo *installed = solv->installed; + Id p2, pp2; + Solvable *s = pool->solvables + p; + Id obs, *obsp; + Id lastp2 = 0; + + if (!solv->keepexplicitobsoletes || !(solv->multiversion.size && MAPTST(&solv->multiversion, p))) + { + FOR_PROVIDES(p2, pp2, s->name) + { + Solvable *ps = pool->solvables + p2; + if (ps->repo != installed) + continue; + if (!pool->implicitobsoleteusesprovides && ps->name != s->name) + continue; + if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + queue_push(q, p2); + lastp2 = p2; + } + } + if (!s->obsoletes) + return; + obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + FOR_PROVIDES(p2, pp2, obs) + { + Solvable *ps = pool->solvables + p2; + if (ps->repo != installed) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + if (p2 == lastp2) + continue; + queue_push(q, p2); + lastp2 = p2; + } +} + +/* + * Call solver_add_obsoleted and intersect the result with the + * elements in Queue q starting at qstart. + * Assumes that it's the first call if qstart == q->count. + * May use auxillary map m for the intersection process, all + * elements of q starting at qstart must have their bit cleared. + * (This is also true after the function returns.) + * (See solver_add_obsoleted for limitations of the package p) + */ +void +solver_intersect_obsoleted(Solver *solv, Id p, Queue *q, int qstart, Map *m) +{ + int i, j; + int qcount = q->count; + + solver_add_obsoleted(solv, p, q); + if (qcount == qstart) + return; /* first call */ + if (qcount == q->count) + j = qstart; + else if (qcount == qstart + 1) + { + /* easy if there's just one element */ + j = qstart; + for (i = qcount; i < q->count; i++) + if (q->elements[i] == q->elements[qstart]) + { + j++; /* keep the element */ + break; + } + } + else if (!m || (!m->size && q->count - qstart <= 8)) + { + /* faster than a map most of the time */ + int k; + for (i = j = qstart; i < qcount; i++) + { + Id ip = q->elements[i]; + for (k = qcount; k < q->count; k++) + if (q->elements[k] == ip) + { + q->elements[j++] = ip; + break; + } + } + } + else + { + /* for the really pathologic cases we use the map */ + Repo *installed = solv->installed; + if (!m->size) + map_init(m, installed->end - installed->start); + for (i = qcount; i < q->count; i++) + MAPSET(m, q->elements[i] - installed->start); + for (i = j = qstart; i < qcount; i++) + if (MAPTST(m, q->elements[i] - installed->start)) + { + MAPCLR(m, q->elements[i] - installed->start); + q->elements[j++] = q->elements[i]; + } + } + queue_truncate(q, j); +} + diff --git a/test/testcases/cleandeps/cleandeps_dup.t b/test/testcases/cleandeps/cleandeps_dup.t index 7002434..afebcc4 100644 --- a/test/testcases/cleandeps/cleandeps_dup.t +++ b/test/testcases/cleandeps/cleandeps_dup.t @@ -13,7 +13,8 @@ system i686 rpm system # check untargeted job distupgrade name A [cleandeps] -result transaction,problems <inline> +result transaction,problems,cleandeps <inline> +#>cleandeps B1-1-1.noarch@system #>erase B1-1-1.noarch@system #>install B2-1-1.noarch@test #>upgrade A-1-1.noarch@system A-2-1.noarch@test @@ -21,7 +22,8 @@ result transaction,problems <inline> # check targeted nextjob job distupgrade name A = 2 [cleandeps] -result transaction,problems <inline> +result transaction,problems,cleandeps <inline> +#>cleandeps B1-1-1.noarch@system #>erase B1-1-1.noarch@system #>install B2-1-1.noarch@test #>upgrade A-1-1.noarch@system A-2-1.noarch@test @@ -29,5 +31,15 @@ result transaction,problems <inline> # check targeted to 1-2 nextjob job distupgrade name A = 1-2 [cleandeps] -result transaction,problems <inline> +result transaction,problems,cleandeps <inline> #>upgrade A-1-1.noarch@system A-1-2.noarch@test + +# check all packages +nextjob +job distupgrade all packages [cleandeps] +result transaction,problems,cleandeps <inline> +#>cleandeps B1-1-1.noarch@system +#>erase B1-1-1.noarch@system +#>install B2-1-1.noarch@test +#>upgrade A-1-1.noarch@system A-2-1.noarch@test + diff --git a/test/testcases/cleandeps/cleandeps_in.t b/test/testcases/cleandeps/cleandeps_in.t index 3edacb5..e77a7f7 100644 --- a/test/testcases/cleandeps/cleandeps_in.t +++ b/test/testcases/cleandeps/cleandeps_in.t @@ -9,7 +9,8 @@ repo test 0 testtags <inline> #>=Pkg: B2 1 1 noarch system i686 rpm system job install name A = 2 [cleandeps] -result transaction,problems <inline> +result transaction,problems,cleandeps <inline> +#>cleandeps B1-1-1.noarch@system #>erase B1-1-1.noarch@system #>install B2-1-1.noarch@test #>upgrade A-1-1.noarch@system A-2-1.noarch@test diff --git a/test/testcases/cleandeps/cleandeps_up.t b/test/testcases/cleandeps/cleandeps_up.t index 9bb26d2..5560a28 100644 --- a/test/testcases/cleandeps/cleandeps_up.t +++ b/test/testcases/cleandeps/cleandeps_up.t @@ -2,6 +2,9 @@ repo system 0 testtags <inline> #>=Pkg: A 1 1 noarch #>=Req: B1 #>=Pkg: B1 1 1 noarch +#>=Pkg: C 1 1 noarch +#>=Rec: D +#>=Pkg: D 1 1 noarch repo test 0 testtags <inline> #>=Pkg: A 1 2 noarch #>=Req: B1 @@ -9,11 +12,15 @@ repo test 0 testtags <inline> #>=Req: B2 = 1 #>=Pkg: B1 1 1 noarch #>=Pkg: B2 1 1 noarch +#>=Pkg: C 1 1 noarch +#>=Rec: D system i686 rpm system # check untargeted job update name A [cleandeps] -result transaction,problems <inline> +job update name C [cleandeps] +result transaction,problems,cleandeps <inline> +#>cleandeps B1-1-1.noarch@system #>erase B1-1-1.noarch@system #>install B2-1-1.noarch@test #>upgrade A-1-1.noarch@system A-2-1.noarch@test @@ -21,7 +28,8 @@ result transaction,problems <inline> # check targeted nextjob job update name A = 2 [cleandeps] -result transaction,problems <inline> +result transaction,problems,cleandeps <inline> +#>cleandeps B1-1-1.noarch@system #>erase B1-1-1.noarch@system #>install B2-1-1.noarch@test #>upgrade A-1-1.noarch@system A-2-1.noarch@test @@ -29,5 +37,14 @@ result transaction,problems <inline> # check targeted to 1-2 nextjob job update name A = 1-2 [cleandeps] -result transaction,problems <inline> +result transaction,problems,cleandeps <inline> #>upgrade A-1-1.noarch@system A-1-2.noarch@test + +# check all packages +nextjob +job update all packages [cleandeps] +result transaction,problems,cleandeps <inline> +#>cleandeps B1-1-1.noarch@system +#>erase B1-1-1.noarch@system +#>install B2-1-1.noarch@test +#>upgrade A-1-1.noarch@system A-2-1.noarch@test diff --git a/test/testcases/distupgrade/dup_multiversion3.t b/test/testcases/distupgrade/dup_multiversion3.t index 8be3190..97eb20f 100644 --- a/test/testcases/distupgrade/dup_multiversion3.t +++ b/test/testcases/distupgrade/dup_multiversion3.t @@ -12,17 +12,18 @@ system i686 * system job multiversion name a job distupgrade all packages result transaction,problems <inline> -#>problem 251f1f35 info nothing provides c needed by a-2-1.i686 -#>problem 251f1f35 solution 2f2d254c allow a-1-1.i686@system +#>problem fc3d647e info nothing provides c needed by a-2-1.i686 +#>problem fc3d647e solution 179b72ed allow a-1-1.i686@system +#>problem fc3d647e solution e5fc66c9 erase a-1-1.i686@system nextjob job multiversion name a job distupgrade repo available result transaction,problems <inline> -#>erase a-1-1.i686@system -#>problem 251f1f35 info nothing provides c needed by a-2-1.i686 -#>problem 251f1f35 solution 2f2d254c allow a-1-1.i686@system +#>problem fc3d647e info nothing provides c needed by a-2-1.i686 +#>problem fc3d647e solution 179b72ed allow a-1-1.i686@system +#>problem fc3d647e solution e5fc66c9 erase a-1-1.i686@system ### same with keeporphans diff --git a/test/testcases/distupgrade/dup_noarchchange.t b/test/testcases/distupgrade/dup_noarchchange.t index f500d9b..8231561 100644 --- a/test/testcases/distupgrade/dup_noarchchange.t +++ b/test/testcases/distupgrade/dup_noarchchange.t @@ -9,7 +9,8 @@ system i686 * system solverflags !dupallowarchchange job distupgrade all packages result transaction,problems <inline> -#>problem c43b1300 info problem with installed package a-1-1.i686 -#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-2-1.i586@available +#>problem 7724e627 info problem with installed package a-1-1.i686 +#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system +#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-2-1.i586@available #>upgrade a-1-1.i686@system a-2-1.i586@available #>upgrade b-1-1.i686@system b-2-1.i686@available diff --git a/test/testcases/distupgrade/dup_orphan3.t b/test/testcases/distupgrade/dup_orphan3.t index 6c04348..361649a 100644 --- a/test/testcases/distupgrade/dup_orphan3.t +++ b/test/testcases/distupgrade/dup_orphan3.t @@ -1,6 +1,6 @@ # test dup with orphaned packages # -# part 3: a is not really an orphan, but cannott be downgraded +# part 3: a is not really an orphan, but cannot be downgraded # repo system 0 testtags <inline> @@ -16,8 +16,9 @@ job distupgrade all packages result transaction,problems <inline> #>downgrade a-1-1.i686@system a-1-0.i686@available #>upgrade b-1-1.i686@system b-2-1.i686@available -#>problem c43b1300 info problem with installed package a-1-1.i686 -#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-1-0.i686@available +#>problem 7724e627 info problem with installed package a-1-1.i686 +#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system +#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-1-0.i686@available nextjob @@ -26,8 +27,9 @@ job distupgrade repo available result transaction,problems <inline> #>downgrade a-1-1.i686@system a-1-0.i686@available #>upgrade b-1-1.i686@system b-2-1.i686@available -#>problem c43b1300 info problem with installed package a-1-1.i686 -#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-1-0.i686@available +#>problem 7724e627 info problem with installed package a-1-1.i686 +#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system +#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-1-0.i686@available ### keeporphans @@ -38,8 +40,9 @@ job distupgrade all packages result transaction,problems <inline> #>downgrade a-1-1.i686@system a-1-0.i686@available #>upgrade b-1-1.i686@system b-2-1.i686@available -#>problem c43b1300 info problem with installed package a-1-1.i686 -#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-1-0.i686@available +#>problem 7724e627 info problem with installed package a-1-1.i686 +#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system +#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-1-0.i686@available nextjob @@ -48,9 +51,9 @@ job distupgrade repo available result transaction,problems <inline> #>downgrade a-1-1.i686@system a-1-0.i686@available #>upgrade b-1-1.i686@system b-2-1.i686@available -#>problem c43b1300 info problem with installed package a-1-1.i686 -#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-1-0.i686@available - +#>problem 7724e627 info problem with installed package a-1-1.i686 +#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system +#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-1-0.i686@available ### same with allowuninstall diff --git a/test/testcases/targeted/targeted_up.t b/test/testcases/targeted/targeted_up.t index 1ab09e4..a3cdd45 100644 --- a/test/testcases/targeted/targeted_up.t +++ b/test/testcases/targeted/targeted_up.t @@ -40,7 +40,7 @@ result transaction,problems <inline> # second check forced to untargeted nextjob solverflags noautotarget -job distupgrade name A = 2-1 +job update name A = 2-1 result transaction,problems <inline> # then targeted to D diff --git a/tools/testsolv.c b/tools/testsolv.c index d751246..e66b340 100644 --- a/tools/testsolv.c +++ b/tools/testsolv.c @@ -79,11 +79,12 @@ main(int argc, char **argv) int c; int ex = 0; const char *list = 0; + int list_with_deps = 0; FILE *fp; const char *p; queue_init(&solq); - while ((c = getopt(argc, argv, "vmrhl:s:T:")) >= 0) + while ((c = getopt(argc, argv, "vmrhL:l:s:T:")) >= 0) { switch (c) { @@ -101,6 +102,11 @@ main(int argc, char **argv) break; case 'l': list = optarg; + list_with_deps = 0; + break; + case 'L': + list = optarg; + list_with_deps = 1; break; case 's': if ((p = strchr(optarg, ':'))) @@ -122,6 +128,8 @@ main(int argc, char **argv) { pool = pool_create(); pool_setdebuglevel(pool, debuglevel); + /* report all errors */ + pool_setdebugmask(pool, pool->debugmask | SOLV_ERROR); fp = fopen(argv[optind], "r"); if (!fp) @@ -153,11 +161,17 @@ main(int argc, char **argv) printf("test %d:\n", multijob++); if (list) { + Id p = 0; int selflags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL|SELECTION_GLOB|SELECTION_FLAT; if (*list == '/') selflags |= SELECTION_FILELIST; queue_empty(&job); - selection_make(pool, &job, list, selflags); + if (list_with_deps) + p = testcase_str2solvid(pool, list); + if (p) + queue_push2(&job, SOLVER_SOLVABLE, p); + else + selection_make(pool, &job, list, selflags); if (!job.elements) printf("No match\n"); else @@ -167,7 +181,34 @@ main(int argc, char **argv) queue_init(&q); selection_solvables(pool, &job, &q); for (i = 0; i < q.count; i++) - printf(" - %s\n", testcase_solvid2str(pool, q.elements[i])); + { + printf(" - %s\n", testcase_solvid2str(pool, q.elements[i])); + if (list_with_deps) + { + int j, k; + const char *vendor; + static Id deps[] = { + SOLVABLE_PROVIDES, SOLVABLE_REQUIRES, SOLVABLE_CONFLICTS, SOLVABLE_OBSOLETES, + SOLVABLE_RECOMMENDS, SOLVABLE_SUGGESTS, SOLVABLE_SUPPLEMENTS, SOLVABLE_ENHANCES, + SOLVABLE_PREREQ_IGNOREINST, + 0 + }; + vendor = pool_lookup_str(pool, q.elements[i], SOLVABLE_VENDOR); + if (vendor) + printf(" %s: %s\n", pool_id2str(pool, SOLVABLE_VENDOR), vendor); + for (j = 0; deps[j]; j++) + { + Queue dq; + queue_init(&dq); + pool_lookup_idarray(pool, q.elements[i], deps[j], &dq); + if (dq.count) + printf(" %s:\n", pool_id2str(pool, deps[j])); + for (k = 0; k < dq.count; k++) + printf(" %s\n", pool_dep2str(pool, dq.elements[k])); + queue_free(&dq); + } + } + } queue_free(&q); } } |