aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-12-05 21:18:44 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-12-05 21:19:06 +0000
commitff5e9baba8780789e5badc94e1da50dd6bc67dba (patch)
tree29ded8ce59b2927207082af4428efb19501cfae7
parent74d54724984bc1b3fa49414995151edfe031e368 (diff)
downloadpaludis-ff5e9baba8780789e5badc94e1da50dd6bc67dba.tar.gz
paludis-ff5e9baba8780789e5badc94e1da50dd6bc67dba.tar.xz
Verify REQUIRED_USE
-rw-r--r--paludis/repositories/e/Makefile.am2
-rw-r--r--paludis/repositories/e/do_pretend_action.cc70
-rw-r--r--paludis/repositories/e/e_repository_TEST_4.cc199
-rwxr-xr-xpaludis/repositories/e/e_repository_TEST_4_setup.sh126
-rw-r--r--paludis/repositories/e/eapis/4.conf5
-rw-r--r--paludis/repositories/e/ebuild/4/Makefile.am1
-rw-r--r--paludis/repositories/e/ebuild/4/builtin_bad_required_use.bash45
-rwxr-xr-xpaludis/repositories/e/ebuild/ebuild.bash1
-rw-r--r--paludis/repositories/e/required_use_verifier.cc197
-rw-r--r--paludis/repositories/e/required_use_verifier.hh58
10 files changed, 700 insertions, 4 deletions
diff --git a/paludis/repositories/e/Makefile.am b/paludis/repositories/e/Makefile.am
index 52fe10f..309fd5a 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 01631fe..3bbd652 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 aec0405..9e87568 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 04f4f44..e13c254 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 3f5730e..6e033c7 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 5f78814..14590a5 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 0000000..e35304d
--- /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 0817f1b..e2d839d 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 0000000..45402ba
--- /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 0000000..5847e5a
--- /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