From 601220982c37f3eb77eb5550608954717fa103b1 Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Fri, 16 Jan 2009 19:19:46 -0800 Subject: Create a structured data type to store version comparison in resolver hints. This commit also introduces the ability to select ranges of versions, not just individual versions, and the ability to compare two hints. All untested. --- src/generic/apt/aptitude_resolver.cc | 113 +++++++++++++++++++- src/generic/apt/aptitude_resolver.h | 202 +++++++++++++++++++++++++++++++---- 2 files changed, 295 insertions(+), 20 deletions(-) diff --git a/src/generic/apt/aptitude_resolver.cc b/src/generic/apt/aptitude_resolver.cc index 63750930..8242a2a6 100644 --- a/src/generic/apt/aptitude_resolver.cc +++ b/src/generic/apt/aptitude_resolver.cc @@ -1,6 +1,6 @@ // aptitude_resolver.cc // -// Copyright (C) 2005, 2008 Daniel Burrows +// Copyright (C) 2005, 2008-2009 Daniel Burrows // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -24,8 +24,119 @@ #include #include +#include #include +int aptitude_resolver::resolver_hint::version_selection::compare(const version_selection &other) const +{ + if(type < other.type) + return -1; + else if(type > other.type) + return 1; + else switch(type) + { + case select_by_archive: + return version_selection_string.compare(other.version_selection_string); + + case select_uninst: + return 0; + + case select_by_version: + if(compare_op < other.compare_op) + return -1; + else if(compare_op > other.compare_op) + return 1; + else + return version_selection_string.compare(other.version_selection_string); + + default: + eassert(!"Internal error: we should never get here."); + return 0; + } +} + +bool aptitude_resolver::resolver_hint::version_selection::matches(const aptitude_resolver_version &ver) const +{ + switch(type) + { + case select_by_archive: + if(ver.get_ver().end()) + return false; + + for(pkgCache::VerFileIterator vf = ver.get_ver().FileList(); + !vf.end(); ++vf) + { + for(pkgCache::PkgFileIterator pf = vf.File(); + !pf.end(); ++pf) + { + if(pf.Archive() == version_selection_string) + return true; + } + } + + return false; + + case select_uninst: + return ver.get_ver().end(); + + case select_by_version: + { + pkgCache::VerIterator real_ver(ver.get_ver()); + if(real_ver.end()) + return false; + + int comparison = + _system->VS->CmpVersion(real_ver.VerStr(), version_selection_string); + + switch(compare_op) + { + case less_than: + return comparison < 0; + + case less_than_or_equal: + return comparison <= 0; + + case equal: + return comparison == 0; + + case greater_than: + return comparison > 0; + + case greater_than_or_equal: + return comparison >= 0; + + default: + eassert(!"Internal error: we should never get here."); + return 0; + } + } + + default: + eassert(!"Internal error: we should never get here."); + return 0; + } +} + +int aptitude_resolver::resolver_hint::compare(const resolver_hint &other) const +{ + if(type < other.type) + return -1; + else if(type > other.type) + return 1; + else if(score < other.score) + return -1; + else if(score > other.score) + return 1; + else + { + const int selection_compare = selection.compare(other.selection); + if(selection_compare != 0) + return selection_compare; + + return aptitude::matching::compare_patterns(target, other.target); + } +} + aptitude_resolver::resolver_hint aptitude_resolver::resolver_hint::parse(const std::string &hint) { std::string action; diff --git a/src/generic/apt/aptitude_resolver.h b/src/generic/apt/aptitude_resolver.h index a15502a7..e6d1d688 100644 --- a/src/generic/apt/aptitude_resolver.h +++ b/src/generic/apt/aptitude_resolver.h @@ -1,7 +1,7 @@ // aptitude_resolver.h -*-c++-*- // // -// Copyright (C) 2005, 2008 Daniel Burrows +// Copyright (C) 2005, 2008-2009 Daniel Burrows // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -86,22 +86,169 @@ public: tweak_score }; + /** \brief Describes which versions are selected by a hint. */ + class version_selection + { + public: + /** \brief Describes what sort of version selection is in use. */ + enum version_selection_type + { + /** \brief Versions are selected by archive. + * + * Any version contained in an archive that equals the + * version selection string will be selected. + */ + select_by_archive, + + /** \brief The non-installed version of the package will be + * matched. + */ + select_uninst, + + /** \brief Versions are selected by version string. + * + * Any version contained in an archive that compares + * correctly to the version selection string (according to + * the comparison operator) will be selected. + */ + select_by_version + }; + + /** \brief Lists the comparison operations that are allowed. */ + enum compare_op_type + { + less_than, + less_than_or_equal, + equal, + greater_than, + greater_than_or_equal + }; + + private: + version_selection_type type; + compare_op_type compare_op; + std::string version_selection_string; + + version_selection(version_selection_type _type, + compare_op_type _compare_op, + const std::string &_version_selection_string) + : type(_type), compare_op(_compare_op), + version_selection_string(_version_selection_string) + { + } + + public: + version_selection() + : type((version_selection_type)-1), + compare_op((compare_op_type)-1), + version_selection_string() + { + } + + /** \brief Create a version selection that selects versions by + * their archive. + * + * \param archive The archive to match; only versions that are + * contained in this archive will be selected. + */ + static version_selection make_archive(const std::string &archive) + { + return version_selection(select_by_archive, (compare_op_type)-1, archive); + } + + /** \brief Create a version selection that selects not-installed + * versions. + */ + static version_selection make_uninst() + { + return version_selection(select_uninst, (compare_op_type)-1, std::string()); + } + + /** \brief Create a version selection that selects versions by + * version number. + * + * \param The version number to compare against. + * \param The operation to use in comparison. For instance, + * use pkgCache::Dep::Less to select only versions + * less than the given version. + */ + static version_selection make_version(const std::string &version, + compare_op_type compare_op) + { + return version_selection(select_by_version, compare_op, version); + } + + /** \brief Test a version against this selector. + * + * \param v The version to test. + * + * \return \b true if v is matched by this selector. + */ + bool matches(const aptitude_resolver_version &v) const; + + /** \brief Compare two version selectors. + * + * \param other The version selector to compare against. + * + * Selectors are arbitrarily arranged in a total ordering. + * + * \return a number less than zero if this selector is less + * than the other selector, a number greater than zero if the + * other selector is greater than this selector, and zero if + * the two selectors are equal. + */ + int compare(const version_selection &other) const; + + bool operator<(const version_selection &other) const { return compare(other) < 0; } + bool operator<=(const version_selection &other) const { return compare(other) <= 0; } + bool operator==(const version_selection &other) const { return compare(other) == 0; } + bool operator>=(const version_selection &other) const { return compare(other) >= 0; } + bool operator>(const version_selection &other) const { return compare(other) > 0; } + + /** \brief Get the type of this selection. */ + version_selection_type get_type() const { return type; } + + /** \brief Get the version selection string of this selection. + * + * Only valid for select_by_archive and select_by_version + * selections. + */ + const std::string &get_version_selection_string() const + { + eassert(type == select_by_archive || type == select_by_version); + + return version_selection_string; + } + + /** \brief Get the comparison operation of this selection. + * + * Only valid for select_by_version selections. + */ + compare_op_type get_version_comparison_operator() const + { + eassert(type == select_by_version); + + return compare_op; + } + }; + private: hint_type type; int score; cwidget::util::ref_ptr target; - std::string version; + version_selection selection; resolver_hint(hint_type _type, int _score, const cwidget::util::ref_ptr &_target, - const std::string &_version) - : type(_type), score(_score), target(_target), version(_version) + version_selection _selection) + : type(_type), score(_score), target(_target), + selection(_selection) { } public: resolver_hint() - : type((hint_type)-1), score(-1), target(NULL), version() + : type((hint_type)-1), score(-1), target(NULL), selection() { } @@ -109,24 +256,24 @@ public: /** \brief Create a hint that rejects a version or versions of a package. */ static resolver_hint make_reject(const cwidget::util::ref_ptr &target, - const std::string &version) + const version_selection &selection) { - return resolver_hint(reject, 0, target, version); + return resolver_hint(reject, 0, target, selection); } /** \brief Create a hint that mandates a version or versions of a package. */ static resolver_hint make_mandate(const cwidget::util::ref_ptr &target, - const std::string &version) + const version_selection &selection) { - return resolver_hint(mandate, 0, target, version); + return resolver_hint(mandate, 0, target, selection); } /** \brief Create a hint that adjust the score of a package. */ static resolver_hint make_tweak_score(const cwidget::util::ref_ptr &target, - const std::string &version, + const version_selection &selection, int score) { - return resolver_hint(tweak_score, score, target, version); + return resolver_hint(tweak_score, score, target, selection); } /** \brief Parse a resolver hint definition. @@ -143,11 +290,32 @@ public: * the removal version) that match TARGET will be selected. If * VERSION has the form "/" then the version of the * package from that archive will be selected. If VERSION is - * ":UNINST" then the not-installed version of the package will be - * selected. + * ":UNINST" then the not-installed version of the package will + * be selected. Finally, VERSION may be ">VERSION2", + * "=VERSION2", ">=VERSION2", "VERSION2" to only apply the hint to versions of the package + * that compare accordingly to the version string. (obviously + * "=VERSION2" is redundant, but it is included for completeness) */ static resolver_hint parse(const std::string &definition); + /** \brief Compare this hint to another hint. + * + * \param other The hint to which this is to be compared. + * + * \return -1 if this is less than other, 0 if the two hints are + * equal, and 1 if this is more than other. + * + * Hints exist in an arbitrary total ordering. + */ + int compare(const resolver_hint &other) const; + + bool operator<(const resolver_hint &other) const { return compare(other) < 0; } + bool operator<=(const resolver_hint &other) const { return compare(other) <= 0; } + bool operator==(const resolver_hint &other) const { return compare(other) == 0; } + bool operator>=(const resolver_hint &other) const { return compare(other) >= 0; } + bool operator>(const resolver_hint &other) const { return compare(other) > 0; } + /** \brief Get the type of this hint. * * \sa hint_type @@ -165,12 +333,8 @@ public: const cwidget::util::ref_ptr & get_target() const { return target; } - /** \brief Return the version selected by this hint. - * - * \todo Perhaps this should just test whether a version matches - * instead? - */ - const std::string &get_version() const { return version; } + /** \brief Return the version selection rule for this hint. */ + const version_selection &get_version_selection() const { return selection; } }; aptitude_resolver(int step_score, int broken_score, -- cgit v1.2.3