diff options
-rw-r--r-- | paludis/dep_list.cc | 146 | ||||
-rw-r--r-- | paludis/dep_list.hh | 13 | ||||
-rw-r--r-- | paludis/dep_list.sr | 1 | ||||
-rw-r--r-- | paludis/dep_list_TEST.cc | 128 | ||||
-rw-r--r-- | src/paludis/command_line.cc | 7 | ||||
-rw-r--r-- | src/paludis/command_line.hh | 2 | ||||
-rw-r--r-- | src/paludis/install.cc | 12 |
7 files changed, 302 insertions, 7 deletions
diff --git a/paludis/dep_list.cc b/paludis/dep_list.cc index eb7bd503c..818c71d03 100644 --- a/paludis/dep_list.cc +++ b/paludis/dep_list.cc @@ -70,6 +70,7 @@ DepListOptions::DepListOptions() : reinstall_scm(dl_reinstall_scm_never), target_type(dl_target_package), upgrade(dl_upgrade_always), + fall_back(dl_fall_back_as_needed_except_targets), installed_deps_pre(dl_deps_discard), installed_deps_runtime(dl_deps_try_post), installed_deps_post(dl_deps_try_post), @@ -98,6 +99,8 @@ namespace paludis MergeList::iterator merge_list_insert_position; long merge_list_generation; + DepAtom::ConstPointer current_top_level_target; + const PackageDatabaseEntry * current_pde() const { if (current_merge_list_entry != merge_list.end()) @@ -110,7 +113,8 @@ namespace paludis opts(o), current_merge_list_entry(merge_list.end()), merge_list_insert_position(merge_list.end()), - merge_list_generation(0) + merge_list_generation(0), + current_top_level_target(0) { } }; @@ -416,7 +420,34 @@ DepList::AddVisitor::visit(const PackageDepAtom * const a) * package targets), otherwise error. */ if (! best_visible_candidate) { - if (already_installed->empty()) + bool can_fall_back; + do + { + switch (d->_imp->opts.fall_back) + { + case dl_fall_back_never: + can_fall_back = false; + continue; + + case dl_fall_back_as_needed_except_targets: + if (! d->_imp->current_pde()) + can_fall_back = false; + else if (already_installed->empty()) + can_fall_back = true; + else + can_fall_back = ! d->is_top_level_target(*already_installed->last()); + + continue; + + case dl_fall_back_as_needed: + can_fall_back = true; + continue; + } + + throw InternalError(PALUDIS_HERE, "Bad fall_back value '" + stringify(d->_imp->opts.fall_back) + "'"); + } while (false); + + if (already_installed->empty() || ! can_fall_back) { if (a->use_requirements_ptr() && d->_imp->env->package_database()->query( a->without_use_requirements(), is_either)) @@ -426,7 +457,6 @@ DepList::AddVisitor::visit(const PackageDepAtom * const a) } else { - // todo: top level Log::get_instance()->message(ll_warning, lc_context, "No visible packages matching '" + stringify(*a) + "', falling back to installed package '" + stringify(*already_installed->last()) + "'"); @@ -579,7 +609,8 @@ DepList::~DepList() void DepList::clear() { - _imp.assign(new Implementation<DepList>(_imp->env, _imp->opts)); + DepListOptions o(options); + _imp.assign(new Implementation<DepList>(_imp->env, o)); } void @@ -593,6 +624,10 @@ void DepList::add(DepAtom::ConstPointer atom) { DepListTransaction transaction(_imp->merge_list, _imp->merge_list_generation); + + Save<DepAtom::ConstPointer> save_current_top_level_target(&_imp->current_top_level_target, + _imp->current_top_level_target ? _imp->current_top_level_target : atom); + AddVisitor visitor(this); atom->accept(&visitor); transaction.commit(); @@ -776,9 +811,25 @@ bool DepList::prefer_installed_over_uninstalled(const PackageDatabaseEntry & installed, const PackageDatabaseEntry & uninstalled) { - if (dl_target_package == _imp->opts.target_type) - if (! _imp->current_pde()) - return false; + do + { + switch (_imp->opts.target_type) + { + case dl_target_package: + if (! _imp->current_pde()) + return false; + + if (is_top_level_target(uninstalled)) + return false; + + continue; + + case dl_target_set: + continue; + } + + throw InternalError(PALUDIS_HERE, "Bad target_type value '" + stringify(_imp->opts.target_type) + "'"); + } while (false); if (dl_reinstall_always == _imp->opts.reinstall) return false; @@ -873,3 +924,84 @@ DepList::end() const return Iterator(_imp->merge_list.end()); } +namespace +{ + struct IsTopLevelTarget : + DepAtomVisitorTypes::ConstVisitor, + std::unary_function<PackageDatabaseEntry, bool> + { + const Environment * const env; + DepAtom::ConstPointer target; + const PackageDatabaseEntry * dbe; + bool matched; + + IsTopLevelTarget(const Environment * const e, DepAtom::ConstPointer t) : + env(e), + target(t), + matched(false) + { + } + + bool operator() (const PackageDatabaseEntry & e) + { + dbe = &e; + matched = false; + target->accept(this); + return matched; + } + + void visit(const AllDepAtom * const a) + { + if (matched) + return; + + std::for_each(a->begin(), a->end(), accept_visitor(this)); + } + + void visit(const PackageDepAtom * const a) + { + if (matched) + return; + + if (match_package(env, a, *dbe)) + matched = true; + } + + void visit(const UseDepAtom * const u) + { + if (matched) + return; + + std::for_each(u->begin(), u->end(), accept_visitor(this)); + } + + void visit(const AnyDepAtom * const a) + { + if (matched) + return; + + std::for_each(a->begin(), a->end(), accept_visitor(this)); + } + + void visit(const BlockDepAtom * const) + { + } + + void visit(const PlainTextDepAtom * const) PALUDIS_ATTRIBUTE((noreturn)) + { + throw InternalError(PALUDIS_HERE, "Got PlainTextDepAtom?"); + } + }; + +} + +bool +DepList::is_top_level_target(const PackageDatabaseEntry & e) const +{ + if (! _imp->current_top_level_target) + throw InternalError(PALUDIS_HERE, "current_top_level_target not set?"); + + IsTopLevelTarget t(_imp->env, _imp->current_top_level_target); + return t(e); +} + diff --git a/paludis/dep_list.hh b/paludis/dep_list.hh index 3e9bbeec2..00719771e 100644 --- a/paludis/dep_list.hh +++ b/paludis/dep_list.hh @@ -59,6 +59,18 @@ namespace paludis }; /** + * When can we fall back to installed? + * + * \ingroup grpdepresolver + */ + enum DepListFallBackOption + { + dl_fall_back_as_needed_except_targets, + dl_fall_back_as_needed, + dl_fall_back_never + }; + + /** * When should we reinstall scm. * * \ingroup grpdepresolver @@ -266,6 +278,7 @@ namespace paludis void add_already_installed_package(const PackageDatabaseEntry &, DepTag::ConstPointer); void add_predeps(DepAtom::ConstPointer, const DepListDepsOption, const std::string &); void add_postdeps(DepAtom::ConstPointer, const DepListDepsOption, const std::string &); + bool is_top_level_target(const PackageDatabaseEntry &) const; public: ///\name Basic operations diff --git a/paludis/dep_list.sr b/paludis/dep_list.sr index 9b0c553f0..691ea4e12 100644 --- a/paludis/dep_list.sr +++ b/paludis/dep_list.sr @@ -7,6 +7,7 @@ make_class_DepListOptions() key reinstall_scm DepListReinstallScmOption key target_type DepListTargetType key upgrade DepListUpgradeOption + key fall_back DepListFallBackOption key installed_deps_pre DepListDepsOption key installed_deps_runtime DepListDepsOption diff --git a/paludis/dep_list_TEST.cc b/paludis/dep_list_TEST.cc index b5a1057c5..bb539f6f2 100644 --- a/paludis/dep_list_TEST.cc +++ b/paludis/dep_list_TEST.cc @@ -1489,5 +1489,133 @@ namespace test_cases TEST_CHECK_EQUAL(join(d.begin(), d.end(), " "), "cat/one-1:0::repo"); } } test_dep_list_forced_downgrade_of_installed; + + /** + * \test Test DepList fall back never. + */ + struct DepListTestCaseFallBackNever : TestCase + { + DepListTestCaseFallBackNever() : TestCase("dep list fall back never") { } + + void run() + { + TestEnvironment env; + + FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo"))); + env.package_database()->add_repository(repo); + repo->add_version("cat", "one", "1")->deps.build_depend_string = "cat/two"; + + FakeInstalledRepository::Pointer installed_repo( + new FakeInstalledRepository(RepositoryName("installed_repo"))); + env.package_database()->add_repository(installed_repo); + installed_repo->add_version("cat", "two", "2"); + + DepList d(&env, DepListOptions()); + d.options.fall_back = dl_fall_back_never; + TEST_CHECK_THROWS(d.add(PortageDepParser::parse("cat/one")), DepListError); + } + } test_dep_list_fall_back_never; + + /** + * \test Test DepList fall back as needed. + */ + struct DepListTestCaseFallBackAsNeeded : TestCase + { + DepListTestCaseFallBackAsNeeded() : TestCase("dep list fall back as needed") { } + + void run() + { + TestEnvironment env; + + FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo"))); + env.package_database()->add_repository(repo); + repo->add_version("cat", "one", "1")->deps.build_depend_string = "cat/two"; + + FakeInstalledRepository::Pointer installed_repo( + new FakeInstalledRepository(RepositoryName("installed_repo"))); + env.package_database()->add_repository(installed_repo); + installed_repo->add_version("cat", "two", "2"); + + DepList d(&env, DepListOptions()); + d.options.fall_back = dl_fall_back_as_needed; + d.add(PortageDepParser::parse("cat/one")); + d.add(PortageDepParser::parse("cat/two")); + TEST_CHECK_EQUAL(join(d.begin(), d.end(), " "), "cat/two-2:0::installed_repo cat/one-1:0::repo"); + } + } test_dep_list_fall_back_as_needed; + + /** + * \test Test DepList fall back as needed. + */ + struct DepListTestCaseFallBackAsNeededNotTargets : TestCase + { + DepListTestCaseFallBackAsNeededNotTargets() : TestCase("dep list fall back as needed not targets") { } + + void run() + { + TestEnvironment env; + + FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo"))); + env.package_database()->add_repository(repo); + repo->add_version("cat", "one", "1")->deps.build_depend_string = "cat/two"; + + FakeInstalledRepository::Pointer installed_repo( + new FakeInstalledRepository(RepositoryName("installed_repo"))); + env.package_database()->add_repository(installed_repo); + installed_repo->add_version("cat", "two", "2"); + installed_repo->add_version("cat", "three", "3"); + + DepList d1(&env, DepListOptions()); + d1.options.fall_back = dl_fall_back_as_needed_except_targets; + d1.add(PortageDepParser::parse("cat/one")); + TEST_CHECK_EQUAL(join(d1.begin(), d1.end(), " "), "cat/two-2:0::installed_repo cat/one-1:0::repo"); + TEST_CHECK_THROWS(d1.add(PortageDepParser::parse("cat/three")), DepListError); + + DepList d2(&env, DepListOptions()); + d2.options.fall_back = dl_fall_back_as_needed_except_targets; + TEST_CHECK_THROWS(d2.add(PortageDepParser::parse("cat/two")), DepListError); + + DepList d3(&env, DepListOptions()); + d3.options.fall_back = dl_fall_back_as_needed_except_targets; + TEST_CHECK_THROWS(d3.add(PortageDepParser::parse("( cat/one cat/two )")), DepListError); + + DepList d4(&env, DepListOptions()); + d4.options.fall_back = dl_fall_back_as_needed_except_targets; + TEST_CHECK_THROWS(d4.add(PortageDepParser::parse("( cat/one cat/three )")), DepListError); + } + } test_dep_list_fall_back_as_needed_not_targets; + + /** + * \test Test DepList upgrade as needed. + */ + struct DepListTestCaseUpgradeAsNeeded : TestCase + { + DepListTestCaseUpgradeAsNeeded() : TestCase("dep list upgrade as needed") { } + + void run() + { + TestEnvironment env; + + FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo"))); + env.package_database()->add_repository(repo); + repo->add_version("cat", "one", "1")->deps.build_depend_string = "cat/two"; + repo->add_version("cat", "two", "2"); + + FakeInstalledRepository::Pointer installed_repo( + new FakeInstalledRepository(RepositoryName("installed_repo"))); + env.package_database()->add_repository(installed_repo); + installed_repo->add_version("cat", "two", "0"); + + DepList d1(&env, DepListOptions()); + d1.options.upgrade = dl_upgrade_as_needed; + d1.add(PortageDepParser::parse("cat/one")); + TEST_CHECK_EQUAL(join(d1.begin(), d1.end(), " "), "cat/two-0:0::installed_repo cat/one-1:0::repo"); + + DepList d2(&env, DepListOptions()); + d2.options.upgrade = dl_upgrade_as_needed; + d2.add(PortageDepParser::parse("( cat/one cat/two )")); + TEST_CHECK_EQUAL(join(d2.begin(), d2.end(), " "), "cat/two-2:0::repo cat/one-1:0::repo"); + } + } test_dep_list_upgrade_as_needed; } diff --git a/src/paludis/command_line.cc b/src/paludis/command_line.cc index 1ffbbc66e..63ac0b8d7 100644 --- a/src/paludis/command_line.cc +++ b/src/paludis/command_line.cc @@ -173,6 +173,13 @@ CommandLine::CommandLine() : ("discard", "Discard"), "error"), + dl_fall_back(&dl_args, "dl-fall-back", '\0', "When to fall back to installed packages", + paludis::args::EnumArg::EnumArgOptions + ("as-needed-except-targets", "Where necessary, but not for target packages") + ("as-needed", "Where necessary, including for target packages") + ("never", "Never"), + "as-needed"), + list_args(this, "List options", "Options relevant for one or more of the --list actions."), a_repository(&list_args, "repository", '\0', "Matches with this repository name only"), diff --git a/src/paludis/command_line.hh b/src/paludis/command_line.hh index ffe7a7329..81d0f8e5c 100644 --- a/src/paludis/command_line.hh +++ b/src/paludis/command_line.hh @@ -219,6 +219,8 @@ class CommandLine : paludis::args::EnumArg dl_circular; + paludis::args::EnumArg dl_fall_back; + /// } /// \name List arguments diff --git a/src/paludis/install.cc b/src/paludis/install.cc index 99fa9cb33..1939bb769 100644 --- a/src/paludis/install.cc +++ b/src/paludis/install.cc @@ -748,6 +748,18 @@ do_install() throw DoHelp("bad value for --dl-circular"); } + if (CommandLine::get_instance()->dl_fall_back.specified()) + { + if (CommandLine::get_instance()->dl_fall_back.argument() == "as-needed-except-targets") + options.fall_back = dl_fall_back_as_needed_except_targets; + else if (CommandLine::get_instance()->dl_fall_back.argument() == "as-needed") + options.fall_back = dl_fall_back_as_needed; + else if (CommandLine::get_instance()->dl_fall_back.argument() == "never") + options.fall_back = dl_fall_back_never; + else + throw DoHelp("bad value for --dl-fall-back"); + } + if (CommandLine::get_instance()->dl_installed_deps_pre.specified()) options.installed_deps_pre = enum_arg_to_dep_list_deps_option( CommandLine::get_instance()->dl_installed_deps_pre); |