diff options
-rw-r--r-- | paludis/repositories/e/Makefile.am | 2 | ||||
-rw-r--r-- | paludis/repositories/e/do_pretend_action.cc | 70 | ||||
-rw-r--r-- | paludis/repositories/e/e_repository_TEST_4.cc | 199 | ||||
-rwxr-xr-x | paludis/repositories/e/e_repository_TEST_4_setup.sh | 126 | ||||
-rw-r--r-- | paludis/repositories/e/eapis/4.conf | 5 | ||||
-rw-r--r-- | paludis/repositories/e/ebuild/4/Makefile.am | 1 | ||||
-rw-r--r-- | paludis/repositories/e/ebuild/4/builtin_bad_required_use.bash | 45 | ||||
-rwxr-xr-x | paludis/repositories/e/ebuild/ebuild.bash | 1 | ||||
-rw-r--r-- | paludis/repositories/e/required_use_verifier.cc | 197 | ||||
-rw-r--r-- | paludis/repositories/e/required_use_verifier.hh | 58 |
10 files changed, 700 insertions, 4 deletions
diff --git a/paludis/repositories/e/Makefile.am b/paludis/repositories/e/Makefile.am index 52fe10f56..309fd5afd 100644 --- a/paludis/repositories/e/Makefile.am +++ b/paludis/repositories/e/Makefile.am @@ -80,6 +80,7 @@ noinst_HEADERS = \ pipe_command_handler.hh \ profile.hh \ profile_file.hh \ + required_use_verifier.hh \ source_uri_finder.hh \ traditional_layout.hh \ traditional_profile.hh \ @@ -148,6 +149,7 @@ libpaludiserepository_la_SOURCES = \ profile.cc \ profile_file.cc \ registration.cc \ + required_use_verifier.cc \ source_uri_finder.cc \ traditional_layout.cc \ traditional_profile.cc \ diff --git a/paludis/repositories/e/do_pretend_action.cc b/paludis/repositories/e/do_pretend_action.cc index 01631fe52..3bbd652d6 100644 --- a/paludis/repositories/e/do_pretend_action.cc +++ b/paludis/repositories/e/do_pretend_action.cc @@ -23,6 +23,7 @@ #include <paludis/repositories/e/eapi_phase.hh> #include <paludis/repositories/e/make_use.hh> #include <paludis/repositories/e/myoptions_requirements_verifier.hh> +#include <paludis/repositories/e/required_use_verifier.hh> #include <paludis/repositories/e/ebuild.hh> #include <paludis/repositories/e/can_skip_phase.hh> #include <paludis/util/indirect_iterator-impl.hh> @@ -51,9 +52,9 @@ paludis::erepository::do_pretend_action( if (! id->eapi()->supported()) return false; - bool result(true); + bool result(true), can_pretend(true); - if (! id->raw_myoptions_key()) + if ((! id->raw_myoptions_key()) && (! id->required_use_key())) if (id->eapi()->supported()->ebuild_phases()->ebuild_pretend().empty()) return result; @@ -141,7 +142,70 @@ paludis::erepository::do_pretend_action( } } - if (id->eapi()->supported()->ebuild_phases()->ebuild_pretend().empty()) + if (id->required_use_key()) + { + RequiredUseVerifier verifier(id); + id->required_use_key()->value()->top()->accept(verifier); + + if (verifier.unmet_requirements() && ! verifier.unmet_requirements()->empty()) + { + EAPIPhases phases(id->eapi()->supported()->ebuild_phases()->ebuild_bad_options()); + if (phases.begin_phases() == phases.end_phases()) + throw InternalError(PALUDIS_HERE, "using myoptions but no ebuild_bad_options phase"); + + for (EAPIPhases::ConstIterator phase(phases.begin_phases()), phase_end(phases.end_phases()) ; + phase != phase_end ; ++phase) + { + if (! output_manager) + output_manager = a.options.make_output_manager()(a); + + EbuildCommandParams command_params(make_named_values<EbuildCommandParams>( + n::builddir() = repo->params().builddir(), + n::clearenv() = phase->option("clearenv"), + n::commands() = join(phase->begin_commands(), phase->end_commands(), " "), + n::distdir() = repo->params().distdir(), + n::ebuild_dir() = repo->layout()->package_directory(id->name()), + n::ebuild_file() = id->fs_location_key()->value(), + n::eclassdirs() = repo->params().eclassdirs(), + n::environment() = env, + n::exlibsdirs() = exlibsdirs, + n::files_dir() = repo->layout()->package_directory(id->name()) / "files", + n::maybe_output_manager() = output_manager, + n::package_builddir() = repo->params().builddir() / (stringify(id->name().category()) + "-" + stringify(id->name().package()) + "-" + stringify(id->version()) + "-bad_options"), + n::package_id() = id, + n::portdir() = + (repo->params().master_repositories() && ! repo->params().master_repositories()->empty()) ? + (*repo->params().master_repositories()->begin())->params().location() : repo->params().location(), + n::root() = a.options.destination()->installed_root_key() ? + stringify(a.options.destination()->installed_root_key()->value()) : + "/", + n::sandbox() = phase->option("sandbox"), + n::sydbox() = phase->option("sydbox"), + n::userpriv() = phase->option("userpriv") && userpriv_ok + )); + + EbuildBadOptionsCommand bad_options_cmd(command_params, + make_named_values<EbuildBadOptionsCommandParams>( + n::expand_vars() = expand_vars, + n::profiles() = repo->params().profiles(), + n::profiles_with_parents() = repo->profile()->profiles_with_parents(), + n::unmet_requirements() = verifier.unmet_requirements(), + n::use() = use, + n::use_expand() = join(repo->profile()->use_expand()->begin(), repo->profile()->use_expand()->end(), " "), + n::use_expand_hidden() = join(repo->profile()->use_expand_hidden()->begin(), repo->profile()->use_expand_hidden()->end(), " ") + )); + + if (! bad_options_cmd()) + throw ActionFailedError("Bad options phase died"); + } + + result = false; + can_pretend = false; + + } + } + + if (id->eapi()->supported()->ebuild_phases()->ebuild_pretend().empty() || ! can_pretend) return result; EAPIPhases phases(id->eapi()->supported()->ebuild_phases()->ebuild_pretend()); diff --git a/paludis/repositories/e/e_repository_TEST_4.cc b/paludis/repositories/e/e_repository_TEST_4.cc index aec0405b5..9e875681c 100644 --- a/paludis/repositories/e/e_repository_TEST_4.cc +++ b/paludis/repositories/e/e_repository_TEST_4.cc @@ -408,5 +408,204 @@ namespace test_cases } } test_e_repository_eapi_4_merge_type_bin; #endif + + struct ERepositoryEAPI4RequiredUseTest : TestCase + { + ERepositoryEAPI4RequiredUseTest() : TestCase("eapi 4 required use") { } + + unsigned max_run_time() const + { + return 3000; + } + + bool repeatable() const + { + return false; + } + + void run() + { + FSPath root(FSPath::cwd() / "e_repository_TEST_4_dir" / "root"); + + TestEnvironment env; + env.set_paludis_command("/bin/false"); + std::shared_ptr<Map<std::string, std::string> > keys(std::make_shared<Map<std::string, std::string>>()); + keys->insert("format", "e"); + keys->insert("names_cache", "/var/empty"); + keys->insert("location", stringify(FSPath::cwd() / "e_repository_TEST_4_dir" / "repo")); + keys->insert("profiles", stringify(FSPath::cwd() / "e_repository_TEST_4_dir" / "repo/profiles/profile")); + keys->insert("layout", "traditional"); + keys->insert("eapi_when_unknown", "0"); + keys->insert("eapi_when_unspecified", "0"); + keys->insert("profile_eapi", "0"); + keys->insert("distdir", stringify(FSPath::cwd() / "e_repository_TEST_4_dir" / "distdir")); + keys->insert("builddir", stringify(FSPath::cwd() / "e_repository_TEST_4_dir" / "build")); + std::shared_ptr<Repository> repo(ERepository::repository_factory_create(&env, + std::bind(from_keys, keys, std::placeholders::_1))); + env.package_database()->add_repository(1, repo); + + std::shared_ptr<FakeInstalledRepository> installed_repo(std::make_shared<FakeInstalledRepository>( + make_named_values<FakeInstalledRepositoryParams>( + n::environment() = &env, + n::name() = RepositoryName("installed"), + n::suitable_destination() = true, + n::supports_uninstall() = true + ))); + env.package_database()->add_repository(2, installed_repo); + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("all good", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-all-good-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(! pretend_action.failed()); + } + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("all empty", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-all-empty-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(! pretend_action.failed()); + } + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("all one not good", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-all-one-not-good-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(pretend_action.failed()); + } + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("any good", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-any-good-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(! pretend_action.failed()); + } + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("any empty", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-any-empty-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(! pretend_action.failed()); + } + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("any none", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-any-none-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(pretend_action.failed()); + } + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("one none", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-one-none-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(pretend_action.failed()); + } + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("one none", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-one-none-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(pretend_action.failed()); + } + + { + PretendAction pretend_action(make_named_values<PretendActionOptions>( + n::destination() = installed_repo, + n::make_output_manager() = &make_standard_output_manager, + n::replacing() = std::make_shared<PackageIDSequence>() + )); + + TestMessageSuffix suffix("one good", true); + const std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches( + PackageDepSpec(parse_user_package_dep_spec("=cat/required-use-one-good-4::test-repo", + &env, { })), { }))]->last()); + TEST_CHECK(bool(id)); + TEST_CHECK_EQUAL(simple_visitor_cast<const MetadataValueKey<std::string> >(**id->find_metadata("EAPI"))->value(), "4"); + id->perform_action(pretend_action); + TEST_CHECK(! pretend_action.failed()); + } + } + } test_e_repository_eapi_4_required_use; } diff --git a/paludis/repositories/e/e_repository_TEST_4_setup.sh b/paludis/repositories/e/e_repository_TEST_4_setup.sh index 04f4f4474..e13c25470 100755 --- a/paludis/repositories/e/e_repository_TEST_4_setup.sh +++ b/paludis/repositories/e/e_repository_TEST_4_setup.sh @@ -1051,6 +1051,132 @@ pkg_setup() { fi } END +mkdir -p "cat/required-use-all-good" || exit 1 +cat << 'END' > cat/required-use-all-good/required-use-all-good-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="enabled1 enabled2 enabled3" +REQUIRED_USE="enabled1 enabled2 enabled3" +S="${WORKDIR}" +END +mkdir -p "cat/required-use-all-empty" || exit 1 +cat << 'END' > cat/required-use-all-empty/required-use-all-empty-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="" +REQUIRED_USE="( ( ( ) ) )" +S="${WORKDIR}" +END +mkdir -p "cat/required-use-all-one-not-good" || exit 1 +cat << 'END' > cat/required-use-all-one-not-good/required-use-all-one-not-good-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="enabled1 disabled2 enabled3" +REQUIRED_USE="enabled1 disabled2 enabled3" +S="${WORKDIR}" +END +mkdir -p "cat/required-use-any-good" || exit 1 +cat << 'END' > cat/required-use-any-good/required-use-any-good-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="disabled1 enabled2 disabled3" +REQUIRED_USE="|| ( disabled1 enabled2 disabled3 )" +S="${WORKDIR}" +END +mkdir -p "cat/required-use-any-none" || exit 1 +cat << 'END' > cat/required-use-any-none/required-use-any-none-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="disabled1 disabled2 disabled3" +REQUIRED_USE="|| ( disabled1 disabled2 disabled3 )" +S="${WORKDIR}" +END +mkdir -p "cat/required-use-any-empty" || exit 1 +cat << 'END' > cat/required-use-any-empty/required-use-any-empty-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="" +REQUIRED_USE="|| ( )" +S="${WORKDIR}" +END +mkdir -p "cat/required-use-one-good" || exit 1 +cat << 'END' > cat/required-use-one-good/required-use-one-good-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="disabled1 enabled2 disabled3" +REQUIRED_USE="^^ ( disabled1 enabled2 disabled3 )" +S="${WORKDIR}" +END +mkdir -p "cat/required-use-one-none" || exit 1 +cat << 'END' > cat/required-use-one-none/required-use-one-none-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="disabled1 disabled2 disabled3" +REQUIRED_USE="^^ ( disabled1 disabled2 disabled3 )" +S="${WORKDIR}" +END +mkdir -p "cat/required-use-one-many" || exit 1 +cat << 'END' > cat/required-use-one-many/required-use-one-many-4.ebuild || exit 1 +EAPI="${PV}" +DESCRIPTION="The Description" +HOMEPAGE="http://example.com/" +SRC_URI="" +SLOT="0" +LICENSE="GPL-2" +KEYWORDS="test" +EAPI="4" +IUSE="disabled1 enabled2 enabled3" +REQUIRED_USE="^^ ( disabled1 enabled2 enabled3 )" +S="${WORKDIR}" +END cd .. cd .. diff --git a/paludis/repositories/e/eapis/4.conf b/paludis/repositories/e/eapis/4.conf index 3f5730ef8..6e033c7dd 100644 --- a/paludis/repositories/e/eapis/4.conf +++ b/paludis/repositories/e/eapis/4.conf @@ -6,7 +6,7 @@ exported_name = 4 can_be_pbin = true is_pbin = false -ebuild_functions = ${ebuild_functions} pkg_pretend +ebuild_functions = ${ebuild_functions} pkg_pretend builtin_bad_required_use ebuild_install = \ skipname=killold : killold ; \ @@ -29,6 +29,9 @@ ebuild_info = \ sandbox userpriv : initmisc infovars info ; \ : tidyup +ebuild_bad_options = \ + sydbox userpriv : bad_required_use + env_aa = env_kv = env_replacing_versions = REPLACING_VERSIONS diff --git a/paludis/repositories/e/ebuild/4/Makefile.am b/paludis/repositories/e/ebuild/4/Makefile.am index 5f7881498..14590a593 100644 --- a/paludis/repositories/e/ebuild/4/Makefile.am +++ b/paludis/repositories/e/ebuild/4/Makefile.am @@ -5,6 +5,7 @@ SUBDIRS = . libexecprog4dir = $(libexecdir)/paludis/4 libexecprog4_SCRIPTS = \ + builtin_bad_required_use.bash \ pkg_pretend.bash \ src_install.bash \ die_functions.bash \ diff --git a/paludis/repositories/e/ebuild/4/builtin_bad_required_use.bash b/paludis/repositories/e/ebuild/4/builtin_bad_required_use.bash new file mode 100644 index 000000000..e35304d59 --- /dev/null +++ b/paludis/repositories/e/ebuild/4/builtin_bad_required_use.bash @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# vim: set sw=4 sts=4 et : + +# Copyright (c) 2007, 2008, 2010 Ciaran McCreesh +# +# This file is part of the Paludis package manager. Paludis is free software; +# you can redistribute it and/or modify it under the terms of the GNU General +# Public License, version 2, as published by the Free Software Foundation. +# +# Paludis is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA + +default_builtin_bad_required_use() +{ + eerror "The following required use constraints are unmet for ${CATEGORY}/${PF}:" + local f + echo "${EX_UNMET_REQUIREMENTS}" | while IFS=$'\n' read f ; do + eerror " ${f}" + done +} + +builtin_bad_required_use() +{ + default "$@" +} + +ebuild_f_bad_required_use() +{ + if hasq "bad_required_use" ${SKIP_FUNCTIONS} ; then + ebuild_section "Skipping builtin_bad_required_use (SKIP_FUNCTIONS)" + else + echo + builtin_bad_required_use + echo + fi + + true +} + diff --git a/paludis/repositories/e/ebuild/ebuild.bash b/paludis/repositories/e/ebuild/ebuild.bash index 0817f1b16..e2d839d1d 100755 --- a/paludis/repositories/e/ebuild/ebuild.bash +++ b/paludis/repositories/e/ebuild/ebuild.bash @@ -201,6 +201,7 @@ done builtin_infovars builtin_init builtin_initrm builtin_initmisc builtin_loadenv builtin_metadata builtin_killold builtin_killoldrm builtin_saveenv builtin_tidyup builtin_tidyuprm builtin_variable + builtin_bad_required_use pkg_config pkg_info pkg_nofetch pkg_postinst pkg_postrm pkg_preinst pkg_prerm pkg_pretend pkg_setup pkg_bad_options src_compile src_configure src_install src_prepare src_test src_unpack diff --git a/paludis/repositories/e/required_use_verifier.cc b/paludis/repositories/e/required_use_verifier.cc new file mode 100644 index 000000000..45402ba09 --- /dev/null +++ b/paludis/repositories/e/required_use_verifier.cc @@ -0,0 +1,197 @@ +/* vim: set sw=4 sts=4 et foldmethod=syntax : */ + +/* + * Copyright (c) 2010 Ciaran McCreesh + * + * This file is part of the Paludis package manager. Paludis is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <paludis/repositories/e/required_use_verifier.hh> +#include <paludis/util/pimp-impl.hh> +#include <paludis/util/indirect_iterator-impl.hh> +#include <paludis/util/accept_visitor.hh> +#include <paludis/util/save.hh> +#include <paludis/util/log.hh> +#include <paludis/action.hh> +#include <paludis/metadata_key.hh> +#include <paludis/choice.hh> +#include <paludis/stringify_formatter.hh> +#include <list> +#include <algorithm> + +using namespace paludis; +using namespace paludis::erepository; + +namespace +{ + struct Met + { + int number_met; + bool any_unmet; + }; +} + +namespace paludis +{ + template <> + struct Imp<RequiredUseVerifier> + { + const std::shared_ptr<const ERepositoryID> id; + std::shared_ptr<Sequence<std::string> > unmet_requirements; + + std::list<Met> stack; + bool top; + + Imp(const std::shared_ptr<const ERepositoryID> & i) : + id(i), + unmet_requirements(std::make_shared<Sequence<std::string>>()), + top(true) + { + stack.push_front(Met{0, false}); + } + }; +} + +RequiredUseVerifier::RequiredUseVerifier(const std::shared_ptr<const ERepositoryID> & id) : + Pimp<RequiredUseVerifier>(id) +{ +} + +RequiredUseVerifier::~RequiredUseVerifier() = default; + +bool +RequiredUseVerifier::matches(const std::string & s) +{ + if (s.empty()) + throw ActionFailedError("Could not verify empty use requirement"); + + if ('!' == s.at(0)) + return ! matches(s.substr(1)); + + if (! _imp->id->choices_key()) + { + Log::get_instance()->message("e.required_use.no_choices", ll_warning, lc_context) + << "ID '" << *_imp->id << "' has no choices, so cannot check that required use constraint '" << s << "' matches"; + return false; + } + + auto c(_imp->id->choices_key()->value()->find_by_name_with_prefix(ChoiceNameWithPrefix(s))); + if (! c) + { + Log::get_instance()->message("e.required_use.no_choice", ll_warning, lc_context) + << "ID '" << *_imp->id << "' has no choice named '" << s << "'', so cannot check that required use constraint '" << s << "' matches"; + return false; + } + + return c->enabled(); +} + +void +RequiredUseVerifier::visit(const RequiredUseSpecTree::NodeType<PlainTextDepSpec>::Type & node) +{ + if (matches(node.spec()->text())) + ++_imp->stack.begin()->number_met; + else + _imp->stack.begin()->any_unmet = true; +} + +void +RequiredUseVerifier::visit(const RequiredUseSpecTree::NodeType<AllDepSpec>::Type & node) +{ + _imp->stack.push_front(Met{0, false}); + { + Save<bool> top(&_imp->top, false); + std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this)); + } + + Met met(*_imp->stack.begin()); + _imp->stack.pop_front(); + + if (met.any_unmet) + _imp->stack.begin()->any_unmet = true; + else + ++_imp->stack.begin()->number_met; + + if (_imp->top) + { + if (_imp->stack.begin()->any_unmet) + _imp->unmet_requirements->push_back(_imp->id->required_use_key()->pretty_print_flat(StringifyFormatter())); + } +} + +void +RequiredUseVerifier::visit(const RequiredUseSpecTree::NodeType<AnyDepSpec>::Type & node) +{ + _imp->stack.push_front(Met{0, false}); + std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this)); + + Met met(*_imp->stack.begin()); + _imp->stack.pop_front(); + + if (met.number_met > 0) + ++_imp->stack.begin()->number_met; + else if (met.any_unmet) + _imp->stack.begin()->any_unmet = true; + else + { + /* || ( disabled? ( bar ) ) and || ( ) are true. yay Portage! */ + ++_imp->stack.begin()->number_met; + } +} + +void +RequiredUseVerifier::visit(const RequiredUseSpecTree::NodeType<ExactlyOneDepSpec>::Type & node) +{ + _imp->stack.push_front(Met{0, false}); + std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this)); + + Met met(*_imp->stack.begin()); + _imp->stack.pop_front(); + + if (met.number_met == 1) + ++_imp->stack.begin()->number_met; + else if (met.number_met > 1) + _imp->stack.begin()->any_unmet = true; + else if (met.any_unmet) + _imp->stack.begin()->any_unmet = true; + else + ++_imp->stack.begin()->number_met; +} + +void +RequiredUseVerifier::visit(const RequiredUseSpecTree::NodeType<ConditionalDepSpec>::Type & node) +{ + if (! node.spec()->condition_met()) + return; + + _imp->stack.push_front(Met{0, false}); + std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this)); + + Met met(*_imp->stack.begin()); + _imp->stack.pop_front(); + + if (met.any_unmet) + _imp->stack.begin()->any_unmet = true; + else + ++_imp->stack.begin()->number_met; +} + +const std::shared_ptr<const Sequence<std::string> > +RequiredUseVerifier::unmet_requirements() const +{ + return _imp->unmet_requirements; +} + +template class Pimp<RequiredUseVerifier>; + diff --git a/paludis/repositories/e/required_use_verifier.hh b/paludis/repositories/e/required_use_verifier.hh new file mode 100644 index 000000000..5847e5afd --- /dev/null +++ b/paludis/repositories/e/required_use_verifier.hh @@ -0,0 +1,58 @@ +/* vim: set sw=4 sts=4 et foldmethod=syntax : */ + +/* + * Copyright (c) 2010 Ciaran McCreesh + * + * This file is part of the Paludis package manager. Paludis is free software; + * you can redistribute it and/or modify it under the terms of the GNU General + * Public License version 2, as published by the Free Software Foundation. + * + * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_E_REQUIRED_USE_VERIFIER_HH +#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_E_REQUIRED_USE_VERIFIER_HH 1 + +#include <paludis/repositories/e/e_repository_id.hh> +#include <paludis/util/sequence.hh> +#include <paludis/util/pimp.hh> +#include <paludis/util/attributes.hh> +#include <paludis/dep_spec.hh> +#include <paludis/spec_tree.hh> +#include <memory> + +namespace paludis +{ + namespace erepository + { + class PALUDIS_VISIBLE RequiredUseVerifier : + private Pimp<RequiredUseVerifier> + { + private: + bool matches(const std::string &); + + public: + RequiredUseVerifier(const std::shared_ptr<const ERepositoryID> &); + ~RequiredUseVerifier(); + + const std::shared_ptr<const Sequence<std::string> > unmet_requirements() const PALUDIS_ATTRIBUTE((warn_unused_result)); + + void visit(const RequiredUseSpecTree::NodeType<PlainTextDepSpec>::Type & node); + void visit(const RequiredUseSpecTree::NodeType<AllDepSpec>::Type & node); + void visit(const RequiredUseSpecTree::NodeType<AnyDepSpec>::Type & node); + void visit(const RequiredUseSpecTree::NodeType<ExactlyOneDepSpec>::Type & node); + void visit(const RequiredUseSpecTree::NodeType<ConditionalDepSpec>::Type & node); + }; + } + + extern template class Pimp<erepository::RequiredUseVerifier>; +} + +#endif |