summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2021-12-23 14:47:19 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2021-12-23 14:47:19 +0900
commit5f5ff9576322b449fca131df791f4748e412df5f (patch)
treefc7068d472fa2fb11cb5ee4d4cf08d48d289c56a
parent4ddbcf346d1185bacbe63dba6e9317424b5206b9 (diff)
downloadlibsolv-5f5ff9576322b449fca131df791f4748e412df5f.tar.gz
libsolv-5f5ff9576322b449fca131df791f4748e412df5f.tar.bz2
libsolv-5f5ff9576322b449fca131df791f4748e412df5f.zip
Imported Upstream version 0.7.20upstream/0.7.20
-rw-r--r--NEWS9
-rw-r--r--VERSION.cmake2
-rw-r--r--bindings/solv.i11
-rw-r--r--doc/libsolv-bindings.txt4
-rw-r--r--ext/repo_comps.c33
-rw-r--r--ext/repo_testcase.c9
-rw-r--r--ext/solv_xmlparser.c44
-rw-r--r--ext/solv_xmlparser.h1
-rw-r--r--ext/testcase.c1
-rw-r--r--package/libsolv.changes15
-rw-r--r--package/libsolv.spec.in12
-rw-r--r--src/conda.c2
-rw-r--r--src/rules.c2
-rw-r--r--src/solver.c62
-rw-r--r--src/solver.h3
-rw-r--r--test/testcases/excludefromweak/excludefromweak-obsoletes.t35
-rw-r--r--test/testcases/excludefromweak/excludefromweak.t24
17 files changed, 258 insertions, 11 deletions
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 <dimstar@opensuse.org>
+
+- 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 <inline>
+#>=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 <inline>
+#>=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 <inline>
+#>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 <inline>
+#>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 <inline>
+
+repo available -99.-1000 testtags <inline>
+#>=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 <inline>
+#>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 <inline>
+#>install pkg-A-1-1.noarch@available