Exheredludis/paludis/repositories/e/myoptions_requirements_verifier.cc
Saleem Abdulrasool 64ba7d5be8 modernize: use default method synthesis
Convert a number of destructors to default synthesized functions.  Try to
inline a few instances into the header.  It should be possible to inline all of
them, however, gcc seems to emit a number of warnings.  Furthermore, some of the
destructors are pure-virtualed, but provide an implementation.  Placing the
definition into the header causes ODR violations.
2016-08-06 11:58:04 -07:00

361 lines
14 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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/myoptions_requirements_verifier.hh>
#include <paludis/repositories/e/e_repository_id.hh>
#include <paludis/repositories/e/eapi.hh>
#include <paludis/repositories/e/myoption.hh>
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/visitor_cast.hh>
#include <paludis/util/tokeniser.hh>
#include <paludis/util/log.hh>
#include <paludis/util/set.hh>
#include <paludis/util/indirect_iterator-impl.hh>
#include <paludis/choice.hh>
#include <paludis/metadata_key.hh>
#include <paludis/dep_spec_annotations.hh>
#include <algorithm>
#include <list>
using namespace paludis;
using namespace paludis::erepository;
typedef std::list<std::pair<ChoicePrefixName, std::string> > ChildrenList;
namespace paludis
{
template <>
struct Imp<MyOptionsRequirementsVerifier>
{
const Environment * const env;
const std::shared_ptr<const ERepositoryID> id;
std::shared_ptr<Sequence<std::string> > unmet_requirements;
std::list<ChoicePrefixName> current_prefix_stack;
std::list<ChildrenList> current_children_stack;
std::list<int> number_enabled_stack;
Imp(
const Environment * const e,
const std::shared_ptr<const ERepositoryID> & i) :
env(e),
id(i),
unmet_requirements(std::make_shared<Sequence<std::string>>())
{
current_prefix_stack.push_front(ChoicePrefixName(""));
current_children_stack.push_front(ChildrenList());
number_enabled_stack.push_front(0);
}
};
}
MyOptionsRequirementsVerifier::MyOptionsRequirementsVerifier(
const Environment * const e,
const std::shared_ptr<const ERepositoryID> & id) :
_imp(e, id)
{
}
MyOptionsRequirementsVerifier::~MyOptionsRequirementsVerifier() = default;
const std::shared_ptr<const Sequence<std::string> >
MyOptionsRequirementsVerifier::unmet_requirements() const
{
return _imp->unmet_requirements;
}
void
MyOptionsRequirementsVerifier::visit(const PlainTextSpecTree::NodeType<PlainTextLabelDepSpec>::Type & node)
{
*_imp->current_prefix_stack.begin() = ChoicePrefixName(node.spec()->label());
}
namespace
{
const std::shared_ptr<const ChoiceValue> find_choice_value(
const std::shared_ptr<const ERepositoryID> & id,
const ChoicePrefixName & prefix,
const ChoiceNameWithPrefix & name_with_prefix)
{
if (id->choices_key())
{
auto choices(id->choices_key()->parse_value());
for (Choices::ConstIterator k(choices->begin()),
k_end(choices->end()) ;
k != k_end ; ++k)
{
if ((*k)->prefix() != prefix)
continue;
for (Choice::ConstIterator i((*k)->begin()), i_end((*k)->end()) ;
i != i_end ; ++i)
if ((*i)->name_with_prefix() == name_with_prefix)
return *i;
}
}
return nullptr;
}
}
void
MyOptionsRequirementsVerifier::verify_one(
const ChoicePrefixName & spec_prefix,
const std::string & spec_text,
const std::shared_ptr<const DepSpecAnnotations> & annotations)
{
std::pair<UnprefixedChoiceName, bool> active_myoption(parse_myoption(spec_text));
ChoiceNameWithPrefix active_flag((
! stringify(spec_prefix).empty() ? stringify(spec_prefix) +
stringify(_imp->id->eapi()->supported()->choices_options()->use_expand_separator()) : "") +
stringify(active_myoption.first));
{
std::shared_ptr<const ChoiceValue> choice_value(find_choice_value(_imp->id, spec_prefix, active_flag));
if (choice_value)
{
if (choice_value->enabled() != active_myoption.second)
return;
}
else
{
/* weird ick */
_imp->unmet_requirements->push_back("Can't work out the state of option '" + stringify(active_flag) + "'");
return;
}
}
for (auto m(annotations->begin()), m_end(annotations->end()) ;
m != m_end ; ++m)
{
switch (m->kind())
{
case dsak_synthetic:
case dsak_expanded:
case dsak_literal:
break;
case dsak_expandable:
continue;
case last_dsak:
throw InternalError(PALUDIS_HERE, "bad dsak");
}
switch (m->role())
{
case dsar_myoptions_requires:
{
std::list<std::string> tokens;
tokenise_whitespace(m->value(), std::back_inserter(tokens));
ChoicePrefixName prefix("");
for (std::list<std::string>::const_iterator t(tokens.begin()), t_end(tokens.end()) ;
t != t_end ; ++t)
{
if (t->empty())
continue;
if (':' == t->at(t->length() - 1))
{
prefix = ChoicePrefixName(t->substr(0, t->length() - 1));
continue;
}
bool req_state(true);
std::string req_flag_s(*t);
if ('-' == req_flag_s.at(0))
{
req_state = false;
req_flag_s.erase(0, 1);
}
UnprefixedChoiceName suffix(req_flag_s);
std::shared_ptr<const ChoiceValue> choice_value;
if (_imp->id->choices_key())
{
auto choices(_imp->id->choices_key()->parse_value());
for (Choices::ConstIterator k(choices->begin()), k_end(choices->end()) ;
k != k_end && ! choice_value ; ++k)
{
if ((*k)->prefix() != prefix)
continue;
for (Choice::ConstIterator i((*k)->begin()), i_end((*k)->end()) ;
i != i_end && ! choice_value ; ++i)
if ((*i)->unprefixed_name() == suffix)
choice_value = *i;
}
}
if (choice_value)
{
if (choice_value->enabled() != req_state)
{
_imp->unmet_requirements->push_back(stringify(active_myoption.second ? "Enabling" : "Disabling") +
" option '" + stringify(active_flag) + "' requires option '" +
stringify(choice_value->name_with_prefix()) + "' to be " +
(req_state ? "enabled" : "disabled"));
}
}
else
{
/* ick */
ChoiceNameWithPrefix qualified_flag_name((stringify(prefix).empty() ? "" : stringify(prefix) +
stringify(_imp->id->eapi()->supported()->choices_options()->use_expand_separator())) + stringify(suffix));
_imp->unmet_requirements->push_back(stringify(active_myoption.second ? "Enabling" : "Disabling") +
" option '" + stringify(active_flag) + "' requires option '" + stringify(qualified_flag_name) + "' to be " +
(req_state ? "enabled" : "disabled") + ", but no such option exists");
}
}
}
break;
default:
break;
}
}
}
void
MyOptionsRequirementsVerifier::visit(const PlainTextSpecTree::NodeType<PlainTextDepSpec>::Type & node)
{
Context context("When verifying requirements for item '" + stringify(*node.spec()) + "':");
for (std::list<ChildrenList>::iterator l(_imp->current_children_stack.begin()), l_end(_imp->current_children_stack.end()) ;
l != l_end ; ++l)
l->push_back(std::make_pair(*_imp->current_prefix_stack.begin(), node.spec()->text()));
{
Context local_context("When finding associated choice:");
std::pair<UnprefixedChoiceName, bool> active_myoption(parse_myoption(node.spec()->text()));
ChoiceNameWithPrefix active_flag((
! stringify(*_imp->current_prefix_stack.begin()).empty() ? stringify(*_imp->current_prefix_stack.begin()) +
stringify(_imp->id->eapi()->supported()->choices_options()->use_expand_separator()) : "") +
stringify(active_myoption.first));
std::shared_ptr<const ChoiceValue> choice_value(find_choice_value(_imp->id, *_imp->current_prefix_stack.begin(), active_flag));
if (choice_value && choice_value->enabled() == active_myoption.second)
for (int & l : _imp->number_enabled_stack)
++l;
}
if ((! node.spec()->maybe_annotations()) || (node.spec()->maybe_annotations()->begin() == node.spec()->maybe_annotations()->end()))
return;
verify_one(*_imp->current_prefix_stack.begin(), node.spec()->text(), node.spec()->maybe_annotations());
}
void
MyOptionsRequirementsVerifier::visit(const PlainTextSpecTree::NodeType<ConditionalDepSpec>::Type & node)
{
if (node.spec()->condition_met(_imp->env, _imp->id))
{
_imp->current_prefix_stack.push_front(*_imp->current_prefix_stack.begin());
_imp->current_children_stack.push_front(ChildrenList());
_imp->number_enabled_stack.push_front(0);
std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this));
_imp->number_enabled_stack.pop_front();
_imp->current_children_stack.pop_front();
_imp->current_prefix_stack.pop_front();
}
}
void
MyOptionsRequirementsVerifier::visit(const PlainTextSpecTree::NodeType<AllDepSpec>::Type & node)
{
_imp->current_prefix_stack.push_front(*_imp->current_prefix_stack.begin());
_imp->current_children_stack.push_front(ChildrenList());
_imp->number_enabled_stack.push_front(0);
std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this));
if (node.spec()->maybe_annotations() && (node.spec()->maybe_annotations()->begin() != node.spec()->maybe_annotations()->end()))
{
for (ChildrenList::const_iterator i(_imp->current_children_stack.begin()->begin()),
i_end(_imp->current_children_stack.begin()->end()) ;
i != i_end ; ++i)
verify_one(i->first, i->second, node.spec()->maybe_annotations());
for (auto m(node.spec()->maybe_annotations()->begin()), m_end(node.spec()->maybe_annotations()->end()) ;
m != m_end ; ++m)
{
switch (m->role())
{
case dsar_myoptions_n_at_least_one:
case dsar_myoptions_n_at_most_one:
case dsar_myoptions_n_exactly_one:
{
std::string children_s;
for (ChildrenList::const_iterator i(_imp->current_children_stack.begin()->begin()),
i_end(_imp->current_children_stack.begin()->end()) ;
i != i_end ; ++i)
{
if (! children_s.empty())
children_s.append(", ");
if (! stringify(i->first).empty())
{
children_s.append(stringify(i->first));
children_s.append(stringify(_imp->id->eapi()->supported()->choices_options()->use_expand_separator()));
}
children_s.append(stringify(i->second));
}
children_s = "( " + children_s + " )";
if (dsar_myoptions_n_at_least_one == m->role())
{
if (*_imp->number_enabled_stack.begin() < 1)
_imp->unmet_requirements->push_back("At least one of options " + children_s + " must be met");
}
else if (dsar_myoptions_n_at_most_one == m->role())
{
if (*_imp->number_enabled_stack.begin() > 1)
_imp->unmet_requirements->push_back("At most one of options " + children_s + " must be met");
}
else if (dsar_myoptions_n_exactly_one == m->role())
{
if (*_imp->number_enabled_stack.begin() != 1)
_imp->unmet_requirements->push_back("Exactly one of options " + children_s + " must be met");
}
else
_imp->unmet_requirements->push_back("Don't know what '" + stringify(m->value()) + "' means");
}
break;
default:
break;
}
}
}
_imp->number_enabled_stack.pop_front();
_imp->current_children_stack.pop_front();
_imp->current_prefix_stack.pop_front();
}
namespace paludis
{
template class Pimp<MyOptionsRequirementsVerifier>;
}