diff options
-rw-r--r-- | apt-pkg/cacheset.cc | 65 | ||||
-rw-r--r-- | apt-pkg/cacheset.h | 10 | ||||
-rw-r--r-- | apt-private/private-cacheset.cc | 162 | ||||
-rw-r--r-- | apt-private/private-cacheset.h | 2 | ||||
-rwxr-xr-x | 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); } @@ -811,6 +823,20 @@ pkgCache::VerIterator CacheSetHelper::canNotGetCandInstVer(pkgCacheFile &Cache, 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); +} + /*}}}*/ /*}}}*/ // showPackageSelection - by selector and given pattern /*{{{*/ void CacheSetHelper::showPackageSelection(pkgCache::PkgIterator const &pkg, enum PkgSelector const select, @@ -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<typename Interface, typename Master, typename iterator_type, typename container_iterator, typename container_value> 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 <config.h> +#include <apt-pkg/cacheset.h> #include <apt-pkg/aptconfiguration.h> #include <apt-pkg/cachefile.h> #include <apt-pkg/cachefilter.h> @@ -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<std::string> 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<std::string> 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 |