From 5eff00606c0c55cb6e456e521c8c4059e37264a0 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Tue, 26 May 2020 15:04:44 +0200 Subject: Allow version selection to match versioned self-provides Edgecase of an edgecase at best, but it works just fine as a dependency, so it should really work on the commandline as well. --- apt-pkg/versionmatch.cc | 15 +++++++------ .../test-bug-758153-versioned-provides-support | 26 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/apt-pkg/versionmatch.cc b/apt-pkg/versionmatch.cc index 340f5a64c..82590edfe 100644 --- a/apt-pkg/versionmatch.cc +++ b/apt-pkg/versionmatch.cc @@ -159,15 +159,16 @@ bool pkgVersionMatch::MatchVer(const char *A,string B,bool Prefix) /* */ pkgCache::VerIterator pkgVersionMatch::Find(pkgCache::PkgIterator Pkg) { - pkgCache::VerIterator Ver = Pkg.VersionList(); - for (; Ver.end() == false; ++Ver) - { + for (auto Ver = Pkg.VersionList(); not Ver.end(); ++Ver) if (VersionMatches(Ver)) return Ver; - } - - // This will be Ended by now. - return Ver; + // check if the package provides itself in a matching version + for (auto Prov = Pkg.ProvidesList(); not Prov.end(); ++Prov) + if (Prov->ProvideVersion != 0 && Prov.OwnerPkg() == Prov.ParentPkg()) + if (MatchVer(Prov.ProvideVersion(), VerStr, VerPrefixMatch) || + ExpressionMatches(VerStr, Prov.ProvideVersion())) + return Prov.OwnerVer(); + return pkgCache::VerIterator{}; } /*}}}*/ diff --git a/test/integration/test-bug-758153-versioned-provides-support b/test/integration/test-bug-758153-versioned-provides-support index fb8ed39e8..7bf9d76bd 100755 --- a/test/integration/test-bug-758153-versioned-provides-support +++ b/test/integration/test-bug-758153-versioned-provides-support @@ -336,6 +336,32 @@ $SOMEPACKAGESCOULDNOT E: Unable to correct problems, you have held broken packages." aptget install needsnext3:i386 -s --solver $solver fi + testsuccessequal "$HEADER +The following NEW packages will be installed: + selfprov +0 upgraded, 1 newly installed, 0 to remove and 2 not upgraded. +Inst selfprov (2 unstable [amd64]) +Conf selfprov (2 unstable [amd64])" apt install selfprov -s --solver $solver + if [ "$solver" = 'apt' ]; then + HEADER_POST_NL=" +$HEADER_POST" + else + HEADER_POST_NL='' + fi + testsuccessequal "$HEADER_PRE +Selected version '2' (unstable [amd64]) for 'selfprov'$HEADER_POST_NL +The following NEW packages will be installed: + selfprov +0 upgraded, 1 newly installed, 0 to remove and 2 not upgraded. +Inst selfprov (2 unstable [amd64]) +Conf selfprov (2 unstable [amd64])" apt install selfprov=1 -s --solver $solver + testsuccessequal "$HEADER +The following NEW packages will be installed: + selfprov +0 upgraded, 1 newly installed, 0 to remove and 2 not upgraded. +Inst selfprov (2 unstable [amd64]) +Conf selfprov (2 unstable [amd64])" apt install selfprov=2 -s --solver $solver + testsuccessequal "$HEADER The following additional packages will be installed: selfprov -- cgit v1.2.3-70-g09d2 From 572810e9f321237873d1536c88991d7825c6f1db Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Tue, 26 May 2020 19:19:38 +0200 Subject: Allow =version and /release selector on virtual packages We already have code for figuring out if a virtual package is only provided by a single provider (and otherwise show a list) we can auto-select for the user, so we can adapt that to work with versioned provides as well and while at it also release selectors. The code tries to keep ABI backward compatible and hence turns relatively ugly as we need a parameter (the selector) to be passed around without adding a parameter or new virtual methods. --- apt-pkg/cacheset.cc | 65 +++++++-- apt-pkg/cacheset.h | 10 +- apt-private/private-cacheset.cc | 162 +++++++++++++++------ apt-private/private-cacheset.h | 2 + test/integration/test-apt-get-install-virtual-pkgs | 74 ++++++++++ 5 files changed, 253 insertions(+), 60 deletions(-) diff --git a/apt-pkg/cacheset.cc b/apt-pkg/cacheset.cc index 565a2b298..e029732f1 100644 --- a/apt-pkg/cacheset.cc +++ b/apt-pkg/cacheset.cc @@ -488,14 +488,12 @@ bool VersionContainerInterface::FromString(VersionContainerInterface * const vci pkgVersionMatch Match(ver, (verIsRel == true ? pkgVersionMatch::Release : pkgVersionMatch::Version)); V = Match.Find(P); - if (V.end() == true) { + helper.setLastVersionMatcher(ver); + if (V.end()) { if (verIsRel == true) - _error->Error(_("Release '%s' for '%s' was not found"), - ver.c_str(), P.FullName(true).c_str()); + V = helper.canNotGetVersion(CacheSetHelper::RELEASE, Cache, P); else - _error->Error(_("Version '%s' for '%s' was not found"), - ver.c_str(), P.FullName(true).c_str()); - continue; + V = helper.canNotGetVersion(CacheSetHelper::VERSIONNUMBER, Cache, P); } } if (V.end() == true) @@ -568,9 +566,25 @@ bool VersionContainerInterface::FromPackage(VersionContainerInterface * const vc helper.canNotFindVersion(CacheSetHelper::NEWEST, vci, Cache, P); break; case CacheSetHelper::RELEASE: + { + pkgVersionMatch Match(helper.getLastVersionMatcher(), pkgVersionMatch::Release); + V = Match.Find(P); + if (not V.end()) + found |= vci->insert(V); + else + helper.canNotFindVersion(CacheSetHelper::RELEASE, vci, Cache, P); + } + break; case CacheSetHelper::VERSIONNUMBER: - // both make no sense here, so always false - return false; + { + pkgVersionMatch Match(helper.getLastVersionMatcher(), pkgVersionMatch::Version); + V = Match.Find(P); + if (not V.end()) + found |= vci->insert(V); + else + helper.canNotFindVersion(CacheSetHelper::VERSIONNUMBER, vci, Cache, P); + } + break; } return found; } @@ -728,10 +742,8 @@ void CacheSetHelper::canNotFindVersion(enum VerSelector const select, VersionCon case CANDIDATE: canNotFindCandidateVer(Cache, Pkg); break; case INSTALLED: canNotFindInstalledVer(Cache, Pkg); break; case CANDANDINST: canNotGetCandInstVer(Cache, Pkg); break; - case RELEASE: - case VERSIONNUMBER: - // invalid in this branch - break; + case RELEASE: canNotGetVerFromRelease(Cache, Pkg, getLastVersionMatcher()); break; + case VERSIONNUMBER: canNotGetVerFromVersionNumber(Cache, Pkg, getLastVersionMatcher()); break; } } // canNotFindAllVer /*{{{*/ @@ -762,10 +774,10 @@ pkgCache::VerIterator CacheSetHelper::canNotGetVersion(enum VerSelector const se case INSTALLED: return canNotFindInstalledVer(Cache, Pkg); case CANDINST: return canNotGetCandInstVer(Cache, Pkg); case INSTCAND: return canNotGetInstCandVer(Cache, Pkg); + case RELEASE: return canNotGetVerFromRelease(Cache, Pkg, getLastVersionMatcher()); + case VERSIONNUMBER: return canNotGetVerFromVersionNumber(Cache, Pkg, getLastVersionMatcher()); case ALL: case CANDANDINST: - case RELEASE: - case VERSIONNUMBER: // invalid in this branch return pkgCache::VerIterator(Cache, 0); } @@ -809,6 +821,20 @@ pkgCache::VerIterator CacheSetHelper::canNotGetCandInstVer(pkgCacheFile &Cache, if (ShowError == true) _error->Insert(ErrorType, _("Can't select installed nor candidate version from package '%s' as it has neither of them"), Pkg.FullName(true).c_str()); return pkgCache::VerIterator(Cache, 0); +} + /*}}}*/ +// canNotFindMatchingVer /*{{{*/ +pkgCache::VerIterator CacheSetHelper::canNotGetVerFromRelease(pkgCacheFile &Cache, + pkgCache::PkgIterator const &Pkg, std::string const &release) { + if (ShowError == true) + _error->Insert(ErrorType, _("Release '%s' for '%s' was not found"), release.c_str(), Pkg.FullName(true).c_str()); + return pkgCache::VerIterator(Cache, 0); +} +pkgCache::VerIterator CacheSetHelper::canNotGetVerFromVersionNumber(pkgCacheFile &Cache, + pkgCache::PkgIterator const &Pkg, std::string const &verstr) { + if (ShowError == true) + _error->Insert(ErrorType, _("Version '%s' for '%s' was not found"), verstr.c_str(), Pkg.FullName(true).c_str()); + return pkgCache::VerIterator(Cache, 0); } /*}}}*/ /*}}}*/ @@ -875,9 +901,16 @@ void CacheSetHelper::showSelectedVersion(pkgCache::PkgIterator const &/*Pkg*/, } /*}}}*/ +class CacheSetHelper::Private { +public: + std::string version_or_release; +}; +std::string CacheSetHelper::getLastVersionMatcher() const { return d->version_or_release; } +void CacheSetHelper::setLastVersionMatcher(std::string const &matcher) { d->version_or_release = matcher; } + CacheSetHelper::CacheSetHelper(bool const ShowError, GlobalError::MsgType ErrorType) : - ShowError(ShowError), ErrorType(ErrorType), d(NULL) {} -CacheSetHelper::~CacheSetHelper() {} + ShowError(ShowError), ErrorType(ErrorType), d(new Private{}) {} +CacheSetHelper::~CacheSetHelper() { delete d; } PackageContainerInterface::PackageContainerInterface() : ConstructedBy(CacheSetHelper::UNKNOWN), d(NULL) {} PackageContainerInterface::PackageContainerInterface(PackageContainerInterface const &by) : PackageContainerInterface() { *this = by; } diff --git a/apt-pkg/cacheset.h b/apt-pkg/cacheset.h index eb9e3e523..383d26b76 100644 --- a/apt-pkg/cacheset.h +++ b/apt-pkg/cacheset.h @@ -157,6 +157,8 @@ public: /*{{{*/ } } + std::string getLastVersionMatcher() const; + void setLastVersionMatcher(std::string const &matcher); /*}}}*/ protected: bool ShowError; @@ -167,6 +169,11 @@ protected: pkgCache::VerIterator canNotGetCandInstVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg); + pkgCache::VerIterator canNotGetVerFromRelease(pkgCacheFile &Cache, + pkgCache::PkgIterator const &Pkg, std::string const &release); + pkgCache::VerIterator canNotGetVerFromVersionNumber(pkgCacheFile &Cache, + pkgCache::PkgIterator const &Pkg, std::string const &verstr); + bool PackageFromTask(PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string pattern); bool PackageFromRegEx(PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string pattern); bool PackageFromFnmatch(PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string pattern); @@ -191,7 +198,8 @@ private: pkgCache::VerIterator canNotFindCandidateVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg); pkgCache::VerIterator canNotFindInstalledVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg); - void * const d; + class Private; + Private * const d; }; /*}}}*/ // Iterator templates for our Containers /*{{{*/ template class Container_iterator_base : diff --git a/apt-private/private-cacheset.cc b/apt-private/private-cacheset.cc index 2a5afac7b..2d85aaf4f 100644 --- a/apt-private/private-cacheset.cc +++ b/apt-private/private-cacheset.cc @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -102,8 +103,21 @@ pkgCache::VerIterator CacheSetHelperVirtuals::canNotGetVersion( pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) { - if (select == NEWEST || select == CANDIDATE || select == ALL) - virtualPkgs.insert(Pkg); + switch (select) + { + case VERSIONNUMBER: + case RELEASE: + case INSTALLED: + case CANDIDATE: + case NEWEST: + case ALL: + virtualPkgs.insert(Pkg); + break; + case CANDANDINST: + case CANDINST: + case INSTCAND: + break; + } return CacheSetHelper::canNotGetVersion(select, Cache, Pkg); } void CacheSetHelperVirtuals::canNotFindVersion( @@ -230,10 +244,11 @@ void CacheSetHelperAPTGet::showVersionSelection(pkgCache::PkgIterator const &Pkg { switch (select) { - case RELEASE: case VERSIONNUMBER: if (pattern == Ver.VerStr()) return; + /* fall through */ + case RELEASE: selectedByRelease.push_back(make_pair(Ver, pattern)); break; default: @@ -258,7 +273,9 @@ bool CacheSetHelperAPTGet::showVirtualPackageErrors(pkgCacheFile &Cache) if (Cache[OPkg].CandidateVerIter(Cache) == I.OwnerVer()) { - c1out << " " << OPkg.FullName(true) << " " << I.OwnerVer().VerStr(); + c1out << " " << OPkg.FullName(true) << ' ' << I.OwnerVer().VerStr(); + if (I->ProvideVersion != 0) + c1out << " (= " << I.ProvideVersion() << ")"; if (Cache[OPkg].Install() == true && Cache[OPkg].NewInstall() == false) c1out << _(" [Installed]"); c1out << std::endl; @@ -268,8 +285,12 @@ bool CacheSetHelperAPTGet::showVirtualPackageErrors(pkgCacheFile &Cache) // if we found no candidate which provide this package, show non-candidates if (provider == 0) for (I = Pkg.ProvidesList(); I.end() == false; ++I) - c1out << " " << I.OwnerPkg().FullName(true) << " " << I.OwnerVer().VerStr() - << _(" [Not candidate version]") << std::endl; + { + c1out << " " << I.OwnerPkg().FullName(true) << " " << I.OwnerVer().VerStr(); + if (I->ProvideVersion != 0) + c1out << " (= " << I.ProvideVersion() << ")"; + c1out << _(" [Not candidate version]") << std::endl; + } else out << _("You should explicitly select one to install.") << std::endl; } else { @@ -305,6 +326,10 @@ pkgCache::VerIterator CacheSetHelperAPTGet::canNotGetVersion(enum VerSelector co return canNotFindNewestVer(Cache, Pkg); case CANDIDATE: return canNotFindCandidateVer(Cache, Pkg); + case VERSIONNUMBER: + return canNotFindVersionNumber(Cache, Pkg, getLastVersionMatcher()); + case RELEASE: + return canNotFindVersionRelease(Cache, Pkg, getLastVersionMatcher()); default: return APT::CacheSetHelper::canNotGetVersion(select, Cache, Pkg); } @@ -324,6 +349,34 @@ void CacheSetHelperAPTGet::canNotFindVersion(enum VerSelector const select, APT: } } +pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindVersionNumber(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, std::string const &verstr) +{ + APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::VERSIONNUMBER); + if (not verset.empty()) + return *(verset.begin()); + else if (ShowError) + { + auto const V = canNotGetVerFromVersionNumber(Cache, Pkg, verstr); + if (not V.end()) + return V; + virtualPkgs.insert(Pkg); + } + return pkgCache::VerIterator(Cache, 0); +} +pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindVersionRelease(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, std::string const &verstr) +{ + APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::RELEASE); + if (not verset.empty()) + return *(verset.begin()); + else if (ShowError) + { + auto const V = canNotGetVerFromRelease(Cache, Pkg, verstr); + if (not V.end()) + return V; + virtualPkgs.insert(Pkg); + } + return pkgCache::VerIterator(Cache, 0); +} pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindCandidateVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) { APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::CANDIDATE); @@ -368,49 +421,72 @@ pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindNewestVer(pkgCacheFile &Ca APT::VersionSet CacheSetHelperAPTGet::tryVirtualPackage(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, CacheSetHelper::VerSelector const select) { - /* This is a pure virtual package and there is a single available - candidate providing it. */ - if (unlikely(Cache[Pkg].CandidateVer != 0) || Pkg->ProvidesList == 0) - return APT::VersionSet(); - - pkgCache::PkgIterator Prov; - bool found_one = false; - for (pkgCache::PrvIterator P = Pkg.ProvidesList(); P; ++P) { - pkgCache::VerIterator const PVer = P.OwnerVer(); - pkgCache::PkgIterator const PPkg = PVer.ParentPkg(); + /* If this is a virtual package see if we have a single matching provider + (ignoring multiple matches from the same package due to e.g. M-A) */ + if (Pkg->ProvidesList == 0) + return APT::VersionSet{}; - /* Ignore versions that are not a candidate. */ - if (Cache[PPkg].CandidateVer != PVer) - continue; + auto const oldShowError = showErrors(false); + APT::VersionVector verset; + auto const lastmatcher = getLastVersionMatcher(); + for (auto P = Pkg.ProvidesList(); not P.end(); ++P) + { + auto V = P.OwnerVer(); + switch (select) + { + case RELEASE: + for (auto File = V.FileList(); not File.end(); ++File) + if (lastmatcher == File.File().Archive() || lastmatcher == File.File().Codename()) + { + verset.push_back(V); + break; + } + break; + case VERSIONNUMBER: + if (P->ProvideVersion != 0 && lastmatcher == P.ProvideVersion()) + verset.push_back(V); + break; + default: + if (Cache[V.ParentPkg()].CandidateVerIter(Cache) == V) + verset.push_back(V); + break; + } + } + // do not change the candidate if we have more than one option for this package + if (select == VERSIONNUMBER || select == RELEASE) + for (auto const &V : verset) + if (std::count_if(verset.begin(), verset.end(), [Pkg = V.ParentPkg()](auto const &v) { return v.ParentPkg() == Pkg; }) == 1) + Cache->SetCandidateVersion(V); + showErrors(oldShowError); - if (found_one == false) { - Prov = PPkg; - found_one = true; - } else if (PPkg != Prov) { - // same group, so it's a foreign package - if (PPkg->Group == Prov->Group) { - // do we already have the requested arch? - if (strcmp(Pkg.Arch(), Prov.Arch()) == 0 || - strcmp(Prov.Arch(), "all") == 0 || - unlikely(strcmp(PPkg.Arch(), Prov.Arch()) == 0)) // packages have only on candidate, but just to be sure - continue; - // see which architecture we prefer more and switch to it - std::vector archs = APT::Configuration::getArchitectures(); - if (std::find(archs.begin(), archs.end(), PPkg.Arch()) < std::find(archs.begin(), archs.end(), Prov.Arch())) - Prov = PPkg; + pkgCache::VerIterator Choosen; + for (auto const &Ver : verset) + { + if (Choosen.end()) + Choosen = Ver; + else + { + auto const ChoosenPkg = Choosen.ParentPkg(); + auto const AltPkg = Ver.ParentPkg(); + // seeing two different packages makes it not simple anymore + if (ChoosenPkg->Group != AltPkg->Group) + return APT::VersionSet{}; + // do we already have the requested arch? + if (strcmp(Pkg.Arch(), ChoosenPkg.Arch()) == 0 || + strcmp(ChoosenPkg.Arch(), "all") == 0) continue; - } - found_one = false; // we found at least two - break; + // see which architecture we prefer more and switch to it + std::vector archs = APT::Configuration::getArchitectures(); + if (std::find(archs.begin(), archs.end(), AltPkg.Arch()) < std::find(archs.begin(), archs.end(), ChoosenPkg.Arch())) + Choosen = Ver; } } + if (Choosen.end()) + return APT::VersionSet{}; - if (found_one == true) { - ioprintf(out, _("Note, selecting '%s' instead of '%s'\n"), - Prov.FullName(true).c_str(), Pkg.FullName(true).c_str()); - return APT::VersionSet::FromPackage(Cache, Prov, select, *this); - } - return APT::VersionSet(); + ioprintf(out, _("Note, selecting '%s' instead of '%s'\n"), + Choosen.ParentPkg().FullName(true).c_str(), Pkg.FullName(true).c_str()); + return { Choosen }; } pkgCache::PkgIterator CacheSetHelperAPTGet::canNotFindPkgName(pkgCacheFile &Cache, std::string const &str) { diff --git a/apt-private/private-cacheset.h b/apt-private/private-cacheset.h index 8848aa3aa..0ff81d5bd 100644 --- a/apt-private/private-cacheset.h +++ b/apt-private/private-cacheset.h @@ -118,6 +118,8 @@ public: void canNotFindVersion(enum VerSelector const select, APT::VersionContainerInterface * const vci, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) APT_OVERRIDE; pkgCache::VerIterator canNotFindCandidateVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg); pkgCache::VerIterator canNotFindNewestVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg); + pkgCache::VerIterator canNotFindVersionNumber(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, std::string const &verstr); + pkgCache::VerIterator canNotFindVersionRelease(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, std::string const &verstr); virtual pkgCache::PkgIterator canNotFindPkgName(pkgCacheFile &Cache, std::string const &str) APT_OVERRIDE; APT::VersionSet tryVirtualPackage(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, diff --git a/test/integration/test-apt-get-install-virtual-pkgs b/test/integration/test-apt-get-install-virtual-pkgs index b2fd7499c..99e01ab2c 100755 --- a/test/integration/test-apt-get-install-virtual-pkgs +++ b/test/integration/test-apt-get-install-virtual-pkgs @@ -23,6 +23,11 @@ insertpackage 'unstable' 'foo4' 'i386' '2' 'Provides: foo-prv4:amd64' insertpackage 'experimental' 'baz5' 'amd64' '1' 'Provides: foo-prv5:amd64' insertpackage 'experimental' 'foo5' 'i386' '2' 'Provides: foo-prv5:amd64' +insertpackage 'stable' 'debhelper' 'amd64,i386' '1' 'Provides: debhelper-compat (= 12)' +insertpackage 'unstable,testing' 'debhelper' 'amd64,i386' '2' 'Provides: debhelper-compat (= 13)' +insertpackage 'experimental' 'debhelper' 'amd64,i386' '3' 'Provides: debhelper-compat (= 13)' +insertpackage 'experimental' 'debhelper-ng' 'amd64,i386' '4' 'Provides: debhelper-compat (= 13)' + setupaptarchive testsuccessequal "Reading package lists... @@ -62,3 +67,72 @@ Package foo-prv5 is a virtual package provided by: baz5 1 [Not candidate version] E: Package 'foo-prv5' has no installation candidate" aptget install foo-prv5 -s + +testsuccessequal 'Reading package lists... +Building dependency tree... +The following NEW packages will be installed: + debhelper +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +Inst debhelper (2 testing, unstable [amd64]) +Conf debhelper (2 testing, unstable [amd64])' apt install debhelper -s +testsuccessequal "Reading package lists... +Building dependency tree... +Note, selecting 'debhelper' instead of 'debhelper-compat' +The following NEW packages will be installed: + debhelper +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +Inst debhelper (2 testing, unstable [amd64]) +Conf debhelper (2 testing, unstable [amd64])" apt install debhelper-compat -s +testsuccessequal "Reading package lists... +Building dependency tree... +Note, selecting 'debhelper' instead of 'debhelper-compat' +Selected version '1' (stable [amd64]) for 'debhelper' +The following NEW packages will be installed: + debhelper +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +Inst debhelper (1 stable [amd64]) +Conf debhelper (1 stable [amd64])" apt install debhelper-compat=12 -s +testsuccessequal "Reading package lists... +Building dependency tree... +Note, selecting 'debhelper' instead of 'debhelper-compat' +Selected version '1' (stable [amd64]) for 'debhelper' +The following NEW packages will be installed: + debhelper +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +Inst debhelper (1 stable [amd64]) +Conf debhelper (1 stable [amd64])" apt install debhelper-compat/stable -s +# by version selection we have selected the experimental debhelper-ng here +# but dehelper stays at the candidate as it already provides 13 +testfailureequal "Reading package lists... +Building dependency tree... +Package debhelper-compat is a virtual package provided by: + debhelper 2 (= 13) + debhelper-ng 4 (= 13) +You should explicitly select one to install. + +E: Version '13' for 'debhelper-compat' was not found" apt install debhelper-compat=13 -s +testsuccessequal "Reading package lists... +Building dependency tree... +Note, selecting 'debhelper' instead of 'debhelper-compat' +Selected version '2' (testing, unstable [amd64]) for 'debhelper' +The following NEW packages will be installed: + debhelper +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +Inst debhelper (2 testing, unstable [amd64]) +Conf debhelper (2 testing, unstable [amd64])" apt install debhelper-compat/unstable -s +testfailureequal "Reading package lists... +Building dependency tree... +Package debhelper-compat is a virtual package provided by: + debhelper-ng 4 (= 13) + debhelper 3 (= 13) +You should explicitly select one to install. + +E: Version '13' for 'debhelper-compat' was not found" apt install debhelper-compat=13 -st experimental +testfailureequal "Reading package lists... +Building dependency tree... +Package debhelper-compat is a virtual package provided by: + debhelper-ng 4 (= 13) + debhelper 3 (= 13) +You should explicitly select one to install. + +E: Release 'experimental' for 'debhelper-compat' was not found" apt install debhelper-compat/experimental -s -- cgit v1.2.3-70-g09d2