From 5f5ff9576322b449fca131df791f4748e412df5f Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 23 Dec 2021 14:47:19 +0900 Subject: [PATCH] Imported Upstream version 0.7.20 --- NEWS | 9 ++++ VERSION.cmake | 2 +- bindings/solv.i | 11 +++- doc/libsolv-bindings.txt | 4 ++ ext/repo_comps.c | 33 +++++++++++- ext/repo_testcase.c | 9 ++++ ext/solv_xmlparser.c | 44 +++++++++++++++ ext/solv_xmlparser.h | 1 + ext/testcase.c | 1 + package/libsolv.changes | 15 ++++++ package/libsolv.spec.in | 12 +++-- src/conda.c | 2 +- src/rules.c | 2 +- src/solver.c | 62 ++++++++++++++++++++++ src/solver.h | 3 ++ .../excludefromweak/excludefromweak-obsoletes.t | 35 ++++++++++++ test/testcases/excludefromweak/excludefromweak.t | 24 +++++++++ 17 files changed, 258 insertions(+), 11 deletions(-) create mode 100644 test/testcases/excludefromweak/excludefromweak-obsoletes.t create mode 100644 test/testcases/excludefromweak/excludefromweak.t diff --git a/NEWS b/NEWS index 1bfc9d0..d99f0ee 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,15 @@ This file contains the major changes between libsolv versions: +Version 0.7.20 +- selected bug fixes: + * fix misparsing of '&' in attributes with libxml2 + * choice rules: treat orphaned packages as newest + * fix compatibility with Python 3.10 +- new features: + * new SOLVER_EXCLUDEFROMWEAK job to ignore pkgs for weak dependencies + * support for environments in comps parser + Version 0.7.19 - selected bug fixes: * fix rare segfault in resolve_jobrules() that could happen diff --git a/VERSION.cmake b/VERSION.cmake index 1cecd64..5587ccf 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "1") SET(LIBSOLV_MAJOR "0") SET(LIBSOLV_MINOR "7") -SET(LIBSOLV_PATCH "19") +SET(LIBSOLV_PATCH "20") diff --git a/bindings/solv.i b/bindings/solv.i index 1882b13..61c6fc8 100644 --- a/bindings/solv.i +++ b/bindings/solv.i @@ -10,6 +10,12 @@ %markfunc Pool "mark_Pool"; #endif +#ifdef SWIGPYTHON +%begin %{ +#define PY_SSIZE_T_CLEAN +%} +#endif + /** ** binaryblob handling **/ @@ -63,13 +69,13 @@ typedef struct { $2 = size; } -%typemap(freearg,noblock=1,match="in") (const unsigned char *str, int len) { +%typemap(freearg,noblock=1,match="in") (const unsigned char *str, size_t len) { if (alloc$argnum == SWIG_NEWOBJ) %delete_array(buf$argnum); } %typemap(out,noblock=1,fragment="SWIG_FromCharPtrAndSize") BinaryBlob { #if defined(SWIGPYTHON) && defined(PYTHON3) - $result = $1.data ? Py_BuildValue("y#", $1.data, $1.len) : SWIG_Py_Void(); + $result = $1.data ? Py_BuildValue("y#", $1.data, (Py_ssize_t)$1.len) : SWIG_Py_Void(); #elif defined(SWIGTCL) Tcl_SetObjResult(interp, $1.data ? Tcl_NewByteArrayObj($1.data, $1.len) : NULL); #else @@ -1277,6 +1283,7 @@ typedef struct { static const Id SOLVER_ALLOWUNINSTALL = SOLVER_ALLOWUNINSTALL; static const Id SOLVER_FAVOR = SOLVER_FAVOR; static const Id SOLVER_DISFAVOR = SOLVER_DISFAVOR; + static const Id SOLVER_EXCLUDEFROMWEAK = SOLVER_EXCLUDEFROMWEAK; static const Id SOLVER_JOBMASK = SOLVER_JOBMASK; static const Id SOLVER_WEAK = SOLVER_WEAK; static const Id SOLVER_ESSENTIAL = SOLVER_ESSENTIAL; diff --git a/doc/libsolv-bindings.txt b/doc/libsolv-bindings.txt index ac112cf..0fa313b 100644 --- a/doc/libsolv-bindings.txt +++ b/doc/libsolv-bindings.txt @@ -2076,6 +2076,10 @@ Avoid the specified packages if the solver encounters an alternative. This can also be used to block recommended or supplemented packages from being installed. +*SOLVER_EXCLUDEFROMWEAK*:: +Avoid the specified packages to satisfy recommended or supplemented dependencies. +Unlike SOLVER_DISFAVOR, it does not interfere with other rules. + *SOLVER_JOBMASK*:: A mask containing all the above action bits. diff --git a/ext/repo_comps.c b/ext/repo_comps.c index 014f89a..633fbaa 100644 --- a/ext/repo_comps.c +++ b/ext/repo_comps.c @@ -54,6 +54,8 @@ enum state { STATE_CDISPLAY_ORDER, STATE_GROUPLIST, STATE_GROUPID, + STATE_ENVIRONMENT, + STATE_OPTIONLIST, NUMSTATES }; @@ -61,6 +63,7 @@ static struct solv_xmlparser_element stateswitches[] = { { STATE_START, "comps", STATE_COMPS, 0 }, { STATE_COMPS, "group", STATE_GROUP, 0 }, { STATE_COMPS, "category", STATE_CATEGORY, 0 }, + { STATE_COMPS, "environment", STATE_ENVIRONMENT, 0 }, { STATE_GROUP, "id", STATE_ID, 1 }, { STATE_GROUP, "name", STATE_NAME, 1 }, { STATE_GROUP, "description", STATE_DESCRIPTION, 1 }, @@ -77,6 +80,13 @@ static struct solv_xmlparser_element stateswitches[] = { { STATE_CATEGORY , "grouplist", STATE_GROUPLIST, 0 }, { STATE_CATEGORY , "display_order", STATE_DISPLAY_ORDER, 1 }, { STATE_GROUPLIST, "groupid", STATE_GROUPID, 1 }, + { STATE_ENVIRONMENT, "id", STATE_ID, 1 }, + { STATE_ENVIRONMENT, "name", STATE_NAME, 1 }, + { STATE_ENVIRONMENT, "description", STATE_DESCRIPTION, 1 }, + { STATE_ENVIRONMENT, "grouplist", STATE_GROUPLIST, 0 }, + { STATE_ENVIRONMENT, "optionlist", STATE_OPTIONLIST, 0 }, + { STATE_ENVIRONMENT, "display_order", STATE_DISPLAY_ORDER, 1 }, + { STATE_OPTIONLIST, "groupid", STATE_GROUPID, 1 }, { NUMSTATES } }; @@ -128,9 +138,15 @@ startElement(struct solv_xmlparser *xmlp, int state, const char *name, const cha { case STATE_GROUP: case STATE_CATEGORY: + case STATE_ENVIRONMENT: s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo)); pd->handle = s - pool->solvables; - pd->kind = state == STATE_GROUP ? "group" : "category"; + if (state == STATE_GROUP) + pd->kind = "group"; + else if (state == STATE_CATEGORY) + pd->kind = "category"; + else + pd->kind = "environment"; pd->isvisible = COMPS_DEFAULT_ISVISIBLE; pd->isdefault = COMPS_DEFAULT_ISDEFAULT; break; @@ -160,6 +176,18 @@ startElement(struct solv_xmlparser *xmlp, int state, const char *name, const cha break; } + case STATE_GROUPLIST: + { + pd->reqtype = SOLVABLE_REQUIRES; + break; + } + + case STATE_OPTIONLIST: + { + pd->reqtype = SOLVABLE_SUGGESTS; + break; + } + default: break; } @@ -177,6 +205,7 @@ endElement(struct solv_xmlparser *xmlp, int state, char *content) { case STATE_GROUP: case STATE_CATEGORY: + case STATE_ENVIRONMENT: if (!s->arch) s->arch = ARCH_NOARCH; if (!s->evr) @@ -211,7 +240,7 @@ endElement(struct solv_xmlparser *xmlp, int state, char *content) case STATE_GROUPID: id = pool_str2id(pd->pool, join2(&pd->jd, "group", ":", content), 1); - s->requires = repo_addid_dep(pd->repo, s->requires, id, 0); + repo_add_idarray(pd->repo, pd->handle, pd->reqtype, id); break; case STATE_USERVISIBLE: diff --git a/ext/repo_testcase.c b/ext/repo_testcase.c index 5cc0327..00f7b54 100644 --- a/ext/repo_testcase.c +++ b/ext/repo_testcase.c @@ -480,6 +480,12 @@ testcase_write_testtags(Repo *repo, FILE *fp) tmp = solvable_lookup_str(s, SOLVABLE_BUILDVERSION); if (tmp) fprintf(fp, "=Bvr: %s\n", tmp); + if (solvable_lookup_idarray(s, SOLVABLE_TRACK_FEATURES, &q)) + { + int i; + for (i = 0; i < q.count; i++) + fprintf(fp, "=Trf: %s\n", pool_id2str(pool, q.elements[i])); + } ti = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0); if (ti) fprintf(fp, "=Tim: %u\n", ti); @@ -707,6 +713,9 @@ testcase_add_testtags(Repo *repo, FILE *fp, int flags) case 'B' << 16 | 'v' << 8 | 'r': repodata_set_str(data, s - pool->solvables, SOLVABLE_BUILDVERSION, line + 6); break; + case 'T' << 16 | 'r' << 8 | 'f': + repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_TRACK_FEATURES, line + 6); + break; default: break; } diff --git a/ext/solv_xmlparser.c b/ext/solv_xmlparser.c index 6292663..87bd096 100644 --- a/ext/solv_xmlparser.c +++ b/ext/solv_xmlparser.c @@ -54,6 +54,47 @@ character_data(void *userData, const XML_Char *s, int len) } #ifdef WITH_LIBXML2 +static void fixup_att_inplace(char *at) +{ + while ((at = strchr(at, '&')) != 0) + { + at++; + if (!memcmp(at, "#38;", 4)) + memmove(at, at + 4, strlen(at + 4) + 1); + } +} + +static const xmlChar **fixup_atts(struct solv_xmlparser *xmlp, const xmlChar **atts) +{ + size_t needsize = 0; + size_t natts; + char **at; + + for (natts = 0; atts[natts]; natts++) + if (strchr((char *)atts[natts], '&')) + needsize += strlen((const char *)atts[natts]) + 1; + if (!needsize) + return atts; + at = xmlp->attsdata = solv_realloc(xmlp->attsdata, (natts + 1) * sizeof(xmlChar *) + needsize); + needsize = (natts + 1) * sizeof(xmlChar *); + for (natts = 0; atts[natts]; natts++) + { + at[natts] = (char *)atts[natts]; + if (strchr(at[natts], '&')) + { + size_t l = strlen(at[natts]) + 1; + memcpy((char *)at + needsize, at[natts], l); + at[natts] = (char *)at + needsize; + needsize += l; + fixup_att_inplace(at[natts]); + } + } + at[natts] = 0; + return (const xmlChar **)at; +} +#endif + +#ifdef WITH_LIBXML2 static void start_element(void *userData, const xmlChar *name, const xmlChar **atts) #else @@ -97,6 +138,8 @@ start_element(void *userData, const char *name, const char **atts) static const char *nullattr; atts = (const xmlChar **)&nullattr; } + else if (xmlp->state != oldstate) + atts = fixup_atts(xmlp, atts); #endif if (xmlp->state != oldstate) xmlp->startelement(xmlp, xmlp->state, el->element, (const char **)atts); @@ -177,6 +220,7 @@ solv_xmlparser_free(struct solv_xmlparser *xmlp) queue_free(&xmlp->elementq); xmlp->content = solv_free(xmlp->content); xmlp->errstr = solv_free(xmlp->errstr); + xmlp->attsdata = solv_free(xmlp->attsdata); } static void diff --git a/ext/solv_xmlparser.h b/ext/solv_xmlparser.h index ced0571..717983f 100644 --- a/ext/solv_xmlparser.h +++ b/ext/solv_xmlparser.h @@ -30,6 +30,7 @@ struct solv_xmlparser { Id *elementhelper; void *parser; + void *attsdata; }; #define SOLV_XMLPARSER_OK 0 diff --git a/ext/testcase.c b/ext/testcase.c index 20b0c48..055452f 100644 --- a/ext/testcase.c +++ b/ext/testcase.c @@ -60,6 +60,7 @@ static struct job2str { { SOLVER_FAVOR, "favor" }, { SOLVER_DISFAVOR, "disfavor" }, { SOLVER_BLACKLIST, "blacklist" }, + { SOLVER_EXCLUDEFROMWEAK, "excludefromweak" }, { 0, 0 } }; diff --git a/package/libsolv.changes b/package/libsolv.changes index 6270b61..c402812 100644 --- a/package/libsolv.changes +++ b/package/libsolv.changes @@ -1,4 +1,19 @@ ------------------------------------------------------------------- +Sat Sep 25 22:45:07 CEST 2021 - mls@suse.de + +- fix misparsing of '&' in attributes with libxml2 +- choice rules: treat orphaned packages as newest [bsc#1190465] +- fix compatibility with Python 3.10 +- new SOLVER_EXCLUDEFROMWEAK job type +- support for environments in comps parser + +------------------------------------------------------------------- +Fri Jul 30 11:43:29 UTC 2021 - Dominique Leuenberger + +- Disable python2 usage on suse_version >= 1550 by default (still + possible to use osc build --with=python). + +------------------------------------------------------------------- Wed Apr 7 14:56:16 CEST 2021 - mls@suse.de - fix rare segfault in resolve_jobrules() that could happen diff --git a/package/libsolv.spec.in b/package/libsolv.spec.in index 0964ad6..5e4e30d 100644 --- a/package/libsolv.spec.in +++ b/package/libsolv.spec.in @@ -1,7 +1,7 @@ # # spec file for package libsolv # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,9 +12,10 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # + %define libname libsolv@LIBSOLV_SOVERSION@ %if 0%{?sle_version} >= 120300 || 0%{?suse_version} >= 1330 || !0%{?suse_version} @@ -37,7 +38,11 @@ # we need at least swig 1.3.40 for the bindings ($typemap support) %if 0%{?suse_version} != 1110 %bcond_without python3 +%if 0%{?suse_version} < 1550 %bcond_without python +%else +%bcond_with python +%endif %bcond_without ruby %bcond_without perl %else @@ -56,7 +61,7 @@ Release: 0 Summary: Package dependency solver using a satisfiability algorithm License: BSD-3-Clause Group: Development/Libraries/C and C++ -Url: https://github.com/openSUSE/libsolv +URL: https://github.com/openSUSE/libsolv Source: libsolv-%{version}.tar.bz2 BuildRequires: cmake BuildRequires: gcc-c++ @@ -112,7 +117,6 @@ BuildRequires: xz-devel BuildRequires: libzstd-devel %endif - %description libsolv is a library for solving packages and reading repositories. The solver uses a satisfiability algorithm. diff --git a/src/conda.c b/src/conda.c index 21ad6bf..6f6a65a 100644 --- a/src/conda.c +++ b/src/conda.c @@ -670,7 +670,7 @@ pool_conda_matchspec(Pool *pool, const char *name) if (build) { *p++ = ' '; - memcpy(p, build, buildend - build); + memmove(p, build, buildend - build); p += buildend - build; } evrid = pool_strn2id(pool, version, p - version, 1); diff --git a/src/rules.c b/src/rules.c index b1b5f09..212df32 100644 --- a/src/rules.c +++ b/src/rules.c @@ -3203,7 +3203,7 @@ solver_choicerulecheck2(Solver *solv, Id pi, Id pt, Queue *q) if (!ur->p) ur = solv->rules + solv->featurerules + (pi - pool->installed->start); if (!ur->p) - return 0; + return 1; /* orphaned, thus newest */ queue_push2(q, pi, 0); FOR_RULELITERALS(p, pp, ur) if (p > 0 && p != pi) diff --git a/src/solver.c b/src/solver.c index 89a2ed1..1dc2c78 100644 --- a/src/solver.c +++ b/src/solver.c @@ -1398,6 +1398,8 @@ solver_free(Solver *solv) map_free(&solv->droporphanedmap); map_free(&solv->cleandepsmap); map_free(&solv->allowuninstallmap); + map_free(&solv->excludefromweakmap); + solv_free(solv->favormap); solv_free(solv->decisionmap); @@ -2202,6 +2204,20 @@ prune_disfavored(Solver *solv, Queue *plist) queue_truncate(plist, j); } +static void +prune_exclude_from_weak(Solver *solv, Queue *plist) +{ + int i, j; + for (i = j = 0; i < plist->count; i++) + { + Id p = plist->elements[i]; + if (!MAPTST(&solv->excludefromweakmap, p)) + plist->elements[j++] = p; + } + if (i != j) + queue_truncate(plist, j); +} + static int resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, int *rerunp) { @@ -2275,6 +2291,8 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i continue; if (solv->havedisfavored && solv->favormap[i] < 0) continue; /* disfavored supplements, do not install */ + if (solv->excludefromweakmap.size && MAPTST(&solv->excludefromweakmap, i)) + continue; /* excluded for weak deps, do not install */ queue_push(dqs, i); } } @@ -2283,6 +2301,10 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i if (dq->count && solv->havedisfavored) prune_disfavored(solv, dq); + /* filter out weak_excluded recommended packages */ + if (solv->excludefromweakmap.size) + prune_exclude_from_weak(solv, dq); + /* filter out all packages obsoleted by installed packages */ /* this is no longer needed if we have (and trust) reverse obsoletes */ if ((dqs->count || dq->count) && solv->installed) @@ -3320,6 +3342,37 @@ add_complex_jobrules(Solver *solv, Id dep, int flags, int jobidx, int weak) #endif static void +solver_add_exclude_from_weak(Solver *solv) +{ + Queue *job = &solv->job; + Pool *pool = solv->pool; + int i; + Id p, pp, how, what, select; +for (i = 0; i < job->count; i += 2) + { + how = job->elements[i]; + if ((how & SOLVER_JOBMASK) != SOLVER_EXCLUDEFROMWEAK) + continue; + if (!solv->excludefromweakmap.size) + map_grow(&solv->excludefromweakmap, pool->nsolvables); + what = job->elements[i + 1]; + select = how & SOLVER_SELECTMASK; + if (select == SOLVER_SOLVABLE_REPO) + { + Repo *repo = pool_id2repo(pool, what); + if (repo) + { + Solvable *s; + FOR_REPO_SOLVABLES(repo, p, s) + MAPSET(&solv->excludefromweakmap, p); + } + } + FOR_JOB_SELECT(p, pp, select, what) + MAPSET(&solv->excludefromweakmap, p); + } +} + +static void setup_favormap(Solver *solv) { Queue *job = &solv->job; @@ -3385,6 +3438,7 @@ solver_solve(Solver *solv, Queue *job) int hasfavorjob = 0; int haslockjob = 0; int hasblacklistjob = 0; + int hasexcludefromweakjob = 0; solve_start = solv_timems(0); @@ -3436,6 +3490,7 @@ solver_solve(Solver *solv, Queue *job) map_zerosize(&solv->droporphanedmap); solv->allowuninstall_all = 0; map_zerosize(&solv->allowuninstallmap); + map_zerosize(&solv->excludefromweakmap); map_zerosize(&solv->cleandepsmap); map_zerosize(&solv->weakrulemap); solv->favormap = solv_free(solv->favormap); @@ -3999,6 +4054,10 @@ solver_solve(Solver *solv, Queue *job) POOL_DEBUG(SOLV_DEBUG_JOB, "job: blacklist %s\n", solver_select2str(pool, select, what)); hasblacklistjob = 1; break; + case SOLVER_EXCLUDEFROMWEAK: + POOL_DEBUG(SOLV_DEBUG_JOB, "job: excludefromweak %s\n", solver_select2str(pool, select, what)); + hasexcludefromweakjob = 1; + break; default: POOL_DEBUG(SOLV_DEBUG_JOB, "job: unknown job\n"); break; @@ -4057,6 +4116,9 @@ solver_solve(Solver *solv, Queue *job) else solv->blackrules = solv->blackrules_end = solv->nrules; + if (hasexcludefromweakjob) + solver_add_exclude_from_weak(solv); + if (solv->havedisfavored && solv->strongrecommends && solv->recommendsruleq) solver_addrecommendsrules(solv); else diff --git a/src/solver.h b/src/solver.h index 2dec259..d30ae36 100644 --- a/src/solver.h +++ b/src/solver.h @@ -208,6 +208,8 @@ struct s_Solver { Map allowuninstallmap; /* ok to uninstall those */ int allowuninstall_all; + Map excludefromweakmap; /* remove them from candidates for supplements and recommends */ + Id *favormap; /* favor job index, > 0: favored, < 0: disfavored */ int havedisfavored; /* do we have disfavored packages? */ @@ -248,6 +250,7 @@ typedef struct s_Solver Solver; #define SOLVER_FAVOR 0x0c00 #define SOLVER_DISFAVOR 0x0d00 #define SOLVER_BLACKLIST 0x0e00 +#define SOLVER_EXCLUDEFROMWEAK 0x1000 #define SOLVER_JOBMASK 0xff00 diff --git a/test/testcases/excludefromweak/excludefromweak-obsoletes.t b/test/testcases/excludefromweak/excludefromweak-obsoletes.t new file mode 100644 index 0000000..adc20d5 --- /dev/null +++ b/test/testcases/excludefromweak/excludefromweak-obsoletes.t @@ -0,0 +1,35 @@ +repo @System 0 testtags +#>=Pkg: pkg-A 1.0 1 noarch +#>=Prv: pkg-A = 1.0-1 +#>=Rec: pkg-C +#>=Pkg: pkg-B 1.0 1 noarch +#>=Prv: pkg-B = 1.0-1 + +repo available -99.-1000 testtags +#>=Pkg: pkg-A 1.0 3 noarch +#>=Prv: pkg-A = 1.0-3 +#>=Rec: pkg-B +#>=Pkg: pkg-B 1.0 2 noarch +#>=Prv: pkg-B = 1.0-2 +#>=Pkg: pkg-C 1.0 1 noarch +#>=Prv: pkg-C = 1.0-1 +#>=Obs: pkg-B + +system x86_64 rpm @System +poolflags implicitobsoleteusescolors +solverflags allowvendorchange keepexplicitobsoletes bestobeypolicy keeporphans yumobsoletes + +job update all packages [forcebest] +job excludefromweak name pkg-C +result transaction,problems +#>erase pkg-B-1.0-1.noarch@@System pkg-C-1.0-1.noarch@available +#>install pkg-C-1.0-1.noarch@available +#>upgrade pkg-A-1.0-1.noarch@@System pkg-A-1.0-3.noarch@available + +nextjob +job update oneof pkg-A-1.0-1.noarch@@System pkg-B-1.0-1.noarch@@System pkg-A-1.0-3.noarch@available pkg-B-1.0-2.noarch@available pkg-C-1.0-1.noarch@available [forcebest,targeted,setevr,setarch] +job excludefromweak name pkg-C +result transaction,problems +#>erase pkg-B-1.0-1.noarch@@System pkg-C-1.0-1.noarch@available +#>install pkg-C-1.0-1.noarch@available +#>upgrade pkg-A-1.0-1.noarch@@System pkg-A-1.0-3.noarch@available diff --git a/test/testcases/excludefromweak/excludefromweak.t b/test/testcases/excludefromweak/excludefromweak.t new file mode 100644 index 0000000..4d6d859 --- /dev/null +++ b/test/testcases/excludefromweak/excludefromweak.t @@ -0,0 +1,24 @@ +repo @System 0 testtags + +repo available -99.-1000 testtags +#>=Pkg: pkg-A 1 1 noarch +#>=Prv: pkg-A = 1-1 +#>=Rec: pkg-B +#>=Pkg: pkg-B 1 1 noarch +#>=Prv: pkg-B = 1-1 + +system x86_64 rpm @System +poolflags implicitobsoleteusescolors +solverflags allowvendorchange keepexplicitobsoletes bestobeypolicy keeporphans yumobsoletes + +job install oneof pkg-A-1-1.noarch@available [forcebest,targeted,setevr,setarch] +job excludefromweak name pkg-B +result transaction,problems +#>install pkg-A-1-1.noarch@available + +nextjob +job install oneof pkg-A-1-1.noarch@available [forcebest,targeted,setevr,setarch] +job excludefromweak name pkg-A +job excludefromweak name pkg-B +result transaction,problems +#>install pkg-A-1-1.noarch@available -- 2.7.4