aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Piotr Jaroszyński <peper@gentoo.org> 2007-10-17 11:11:43 +0000
committerAvatar Piotr Jaroszyński <peper@gentoo.org> 2007-10-17 11:11:43 +0000
commit420df001fecba53ad9cd81cffaa65be2f87725bb (patch)
treef492c9ac918c7f09c5afbe5202fdd14a25d0fa40
parent0cde85a9481510c39b04602a19b010769ce294b4 (diff)
downloadpaludis-420df001fecba53ad9cd81cffaa65be2f87725bb.tar.gz
paludis-420df001fecba53ad9cd81cffaa65be2f87725bb.tar.xz
Better ambiguity resolution for virtuals, installed packages. Fixes: ticket:391
-rw-r--r--paludis/package_database.cc101
-rw-r--r--paludis/package_database_TEST.cc66
-rw-r--r--paludis/repositories/e/e_repository.cc8
-rw-r--r--paludis/repositories/e/e_repository.hh2
-rw-r--r--paludis/repository.cc7
-rw-r--r--paludis/repository.hh5
6 files changed, 183 insertions, 6 deletions
diff --git a/paludis/package_database.cc b/paludis/package_database.cc
index bbef21e..0e93118 100644
--- a/paludis/package_database.cc
+++ b/paludis/package_database.cc
@@ -21,12 +21,16 @@
#include <paludis/match_package.hh>
#include <paludis/package_database.hh>
#include <paludis/package_id.hh>
+#include <paludis/environment.hh>
+#include <paludis/util/log.hh>
#include <paludis/util/iterator.hh>
#include <paludis/util/private_implementation_pattern-impl.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/join.hh>
#include <paludis/util/iterator.hh>
#include <paludis/util/set.hh>
+#include <paludis/util/map.hh>
+#include <paludis/util/map-impl.hh>
#include <paludis/util/sequence.hh>
#include <paludis/util/sequence-impl.hh>
#include <paludis/util/tr1_functional.hh>
@@ -187,13 +191,57 @@ PackageDatabase::add_repository(int i, const tr1::shared_ptr<Repository> r)
_imp->repository_importances.insert(std::make_pair(i, _imp->repositories.insert(q, r)));
}
+namespace
+{
+ struct IsInstalled
+ {
+ const FSEntry _root;
+ const tr1::shared_ptr<const PackageDatabase> _db;
+
+ IsInstalled(const Environment * e) :
+ _root(e->root()),
+ _db(e->package_database())
+ {
+ }
+
+ typedef QualifiedPackageName argument_type;
+ typedef bool result_type;
+
+ bool operator() (const QualifiedPackageName & qpn) const
+ {
+ return (! _db->query(query::Package(qpn) & query::InstalledAtRoot(_root), qo_whatever)->empty());
+ }
+ };
+
+ struct IsImportant
+ {
+ typedef QualifiedPackageName argument_type;
+ typedef bool result_type;
+
+ typedef Map<const QualifiedPackageName, const tr1::shared_ptr<const CategoryNamePartSet> > QPNCMap;
+ const tr1::shared_ptr<QPNCMap> _map;
+
+ IsImportant(const tr1::shared_ptr<QPNCMap> & m) :
+ _map(m)
+ {
+ }
+
+ bool operator() (const QualifiedPackageName & qpn) const
+ {
+ return (_map->find(qpn)->second->end() == _map->find(qpn)->second->find(qpn.category));
+ }
+ };
+}
+
QualifiedPackageName
PackageDatabase::fetch_unique_qualified_package_name(
const PackageNamePart & p) const
{
Context context("When disambiguating package name '" + stringify(p) + "':");
- tr1::shared_ptr<QualifiedPackageNameSet> result(new QualifiedPackageNameSet);
+ // Map matching QualifiedPackageNames with unimportant_category_names sets from their repository.
+ typedef Map<const QualifiedPackageName, const tr1::shared_ptr<const CategoryNamePartSet> > QPNCMap;
+ tr1::shared_ptr<QPNCMap> result(new QPNCMap);
for (IndirectIterator<RepositoryConstIterator> r(begin_repositories()), r_end(end_repositories()) ;
r != r_end ; ++r)
@@ -201,17 +249,62 @@ PackageDatabase::fetch_unique_qualified_package_name(
Context local_context("When looking in repository '" + stringify(r->name()) + "':");
tr1::shared_ptr<const CategoryNamePartSet> cats(r->category_names_containing_package(p));
+ tr1::shared_ptr<const CategoryNamePartSet> unimportant_cats(r->unimportant_category_names());
+
for (CategoryNamePartSet::ConstIterator c(cats->begin()), c_end(cats->end()) ;
c != c_end ; ++c)
- result->insert(*c + p);
+ result->insert(*c + p, unimportant_cats);
}
if (result->empty())
throw NoSuchPackageError(stringify(p));
if (result->size() > 1)
- throw AmbiguousPackageNameError(stringify(p), result->begin(), result->end());
+ {
+ using namespace tr1::placeholders;
+
+ std::list<QualifiedPackageName> qpns;
+
+ do
+ {
+ const IsImportant is_important(result);
+ const IsInstalled is_installed(_imp->environment);
+
+ std::remove_copy_if(first_iterator(result->begin()), first_iterator(result->end()),
+ std::front_inserter(qpns),
+ tr1::bind(std::logical_and<bool>(),
+ tr1::bind(std::not1(is_important), _1),
+ tr1::bind(std::not1(is_installed), _1)));
+
+ if (! qpns.empty() && next(qpns.begin()) == qpns.end())
+ break;
- return *(result->begin());
+ qpns.remove_if(tr1::bind(std::logical_and<bool>(),
+ tr1::bind(is_important, _1),
+ tr1::bind(std::not1(is_installed), _1)));
+
+ if (! qpns.empty() && next(qpns.begin()) == qpns.end())
+ break;
+
+ qpns.remove_if(tr1::bind(std::logical_and<bool>(),
+ tr1::bind(std::not1(is_important), _1),
+ tr1::bind(is_installed, _1)));
+
+ if (! qpns.empty() && next(qpns.begin()) == qpns.end())
+ break;
+
+ throw AmbiguousPackageNameError(stringify(p), first_iterator(result->begin()),
+ first_iterator(result->end()));
+ } while (false);
+
+ Log::get_instance()->message(ll_warning, lc_context)
+ << "Package name '" << p << "' is amibguous, assuming you meant '" << *qpns.begin()
+ << "' (candidates were '"
+ << join(first_iterator(result->begin()), first_iterator(result->end()), "', '") << "')";
+
+ return *qpns.begin();
+ }
+ else
+ return result->begin()->first;
}
const tr1::shared_ptr<const PackageIDSequence>
diff --git a/paludis/package_database_TEST.cc b/paludis/package_database_TEST.cc
index 7102d44..82ac55d 100644
--- a/paludis/package_database_TEST.cc
+++ b/paludis/package_database_TEST.cc
@@ -21,8 +21,10 @@
#include <paludis/query.hh>
#include <paludis/environments/test/test_environment.hh>
#include <paludis/repositories/fake/fake_repository.hh>
+#include <paludis/repositories/fake/fake_installed_repository.hh>
#include <paludis/repositories/fake/fake_package_id.hh>
#include <paludis/util/sequence.hh>
+#include <paludis/util/set.hh>
#include <libwrapiter/libwrapiter_forward_iterator.hh>
#include <libwrapiter/libwrapiter_output_iterator.hh>
#include <test/test_framework.hh>
@@ -253,6 +255,23 @@ namespace test_cases
{
PackageDatabaseDisambiguateTest() : TestCase("package database disambiguate") { }
+ struct CoolFakeRepository :
+ FakeRepository
+ {
+ CoolFakeRepository(const Environment * const e, const RepositoryName & rn) :
+ FakeRepository(e, rn)
+ {
+ }
+
+ tr1::shared_ptr<const CategoryNamePartSet> unimportant_category_names() const
+ {
+ tr1::shared_ptr<CategoryNamePartSet> result(new CategoryNamePartSet);
+ result->insert(CategoryNamePart("bad-cat1"));
+ result->insert(CategoryNamePart("bad-cat2"));
+ return result;
+ };
+ };
+
void run()
{
TestEnvironment e;
@@ -267,23 +286,66 @@ namespace test_cases
TEST_CHECK(true);
tr1::shared_ptr<FakeRepository> r2(new FakeRepository(&e, RepositoryName("repo2")));
- r1->add_package(CategoryNamePart("cat-three") + PackageNamePart("pkg-three"));
- r1->add_package(CategoryNamePart("cat-three") + PackageNamePart("pkg-four"));
+ r2->add_package(CategoryNamePart("cat-three") + PackageNamePart("pkg-three"));
+ r2->add_package(CategoryNamePart("cat-three") + PackageNamePart("pkg-four"));
p.add_repository(10, r2);
TEST_CHECK(true);
+ tr1::shared_ptr<FakeRepository> r3(new CoolFakeRepository(&e, RepositoryName("repo3")));
+ r3->add_package(CategoryNamePart("bad-cat1") + PackageNamePart("pkg-important"));
+ r3->add_package(CategoryNamePart("good-cat1") + PackageNamePart("pkg-important"));
+
+ r3->add_package(CategoryNamePart("good-cat1") + PackageNamePart("pkg-installed"));
+ r3->add_package(CategoryNamePart("good-cat2") + PackageNamePart("pkg-installed"));
+
+ r3->add_package(CategoryNamePart("bad-cat1") + PackageNamePart("pkg-fail1"));
+ r3->add_package(CategoryNamePart("bad-cat2") + PackageNamePart("pkg-fail1"));
+
+ r3->add_package(CategoryNamePart("bad-cat1") + PackageNamePart("pkg-fail2"));
+ r3->add_package(CategoryNamePart("bad-cat2") + PackageNamePart("pkg-fail2"));
+
+ r3->add_package(CategoryNamePart("good-cat1") + PackageNamePart("pkg-fail3"));
+ r3->add_package(CategoryNamePart("good-cat2") + PackageNamePart("pkg-fail3"));
+
+ r3->add_package(CategoryNamePart("good-cat1") + PackageNamePart("pkg-fail4"));
+ r3->add_package(CategoryNamePart("good-cat2") + PackageNamePart("pkg-fail4"));
+ p.add_repository(10, r3);
+ TEST_CHECK(true);
+
+ tr1::shared_ptr<FakeInstalledRepository> r4(new FakeInstalledRepository(&e, RepositoryName("repo4")));
+ r4->add_version(CategoryNamePart("good-cat1") + PackageNamePart("pkg-installed"), VersionSpec("0"));
+ r4->add_version(CategoryNamePart("good-cat1") + PackageNamePart("pkg-fail4"), VersionSpec("0"));
+ r4->add_version(CategoryNamePart("good-cat2") + PackageNamePart("pkg-fail4"), VersionSpec("0"));
+ p.add_repository(10, r4);
+
TEST_CHECK_STRINGIFY_EQUAL(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-one")),
"cat-one/pkg-one");
TEST_CHECK_STRINGIFY_EQUAL(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-four")),
"cat-three/pkg-four");
+ TEST_CHECK_STRINGIFY_EQUAL(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-important")),
+ "good-cat1/pkg-important");
+
+ TEST_CHECK_STRINGIFY_EQUAL(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-installed")),
+ "good-cat1/pkg-installed");
+
TEST_CHECK_THROWS(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-two")),
AmbiguousPackageNameError);
TEST_CHECK_THROWS(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-three")),
AmbiguousPackageNameError);
+ TEST_CHECK_THROWS(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-fail1")),
+ AmbiguousPackageNameError);
+ TEST_CHECK_THROWS(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-fail2")),
+ AmbiguousPackageNameError);
+ TEST_CHECK_THROWS(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-fail3")),
+ AmbiguousPackageNameError);
+ TEST_CHECK_THROWS(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-fail4")),
+ AmbiguousPackageNameError);
+
TEST_CHECK_THROWS(p.fetch_unique_qualified_package_name(PackageNamePart("pkg-five")),
NoSuchPackageError);
+
}
} package_database_disambiguate_test;
}
diff --git a/paludis/repositories/e/e_repository.cc b/paludis/repositories/e/e_repository.cc
index 8bfc6d5..9dc1311 100644
--- a/paludis/repositories/e/e_repository.cc
+++ b/paludis/repositories/e/e_repository.cc
@@ -1010,6 +1010,14 @@ ERepository::perform_hook(const Hook & hook) const
return HookResult(0, "");
}
+tr1::shared_ptr<const CategoryNamePartSet>
+ERepository::unimportant_category_names() const
+{
+ tr1::shared_ptr<CategoryNamePartSet> result(make_shared_ptr(new CategoryNamePartSet));
+ result->insert(CategoryNamePart("virtual"));
+ return result;
+}
+
#ifdef ENABLE_QA
namespace
{
diff --git a/paludis/repositories/e/e_repository.hh b/paludis/repositories/e/e_repository.hh
index 4481b5b..3db379a 100644
--- a/paludis/repositories/e/e_repository.hh
+++ b/paludis/repositories/e/e_repository.hh
@@ -225,6 +225,8 @@ namespace paludis
HookResult perform_hook(const Hook &) const
PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual tr1::shared_ptr<const CategoryNamePartSet> unimportant_category_names() const;
+
/**
* Update GLEP 42 news files.
*/
diff --git a/paludis/repository.cc b/paludis/repository.cc
index 6612809..fae91dd 100644
--- a/paludis/repository.cc
+++ b/paludis/repository.cc
@@ -24,6 +24,7 @@
#include <paludis/util/private_implementation_pattern-impl.hh>
#include <paludis/util/instantiation_policy-impl.hh>
#include <paludis/util/stringify.hh>
+#include <paludis/util/make_shared_ptr.hh>
#include <paludis/util/options.hh>
#include <paludis/util/sequence.hh>
#include <paludis/util/sequence-impl.hh>
@@ -299,6 +300,12 @@ Repository::category_names() const
return do_category_names();
}
+tr1::shared_ptr<const CategoryNamePartSet>
+Repository::unimportant_category_names() const
+{
+ return make_shared_ptr(new CategoryNamePartSet);
+}
+
bool
RepositorySyncableInterface::sync() const
{
diff --git a/paludis/repository.hh b/paludis/repository.hh
index f9b508b..dfcbde5 100644
--- a/paludis/repository.hh
+++ b/paludis/repository.hh
@@ -197,6 +197,11 @@ namespace paludis
tr1::shared_ptr<const CategoryNamePartSet> category_names() const;
/**
+ * Fetch unimportant categories.
+ */
+ virtual tr1::shared_ptr<const CategoryNamePartSet> unimportant_category_names() const;
+
+ /**
* Fetch categories that contain a named package.
*/
tr1::shared_ptr<const CategoryNamePartSet> category_names_containing_package(