From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id AF9171381F3 for ; Thu, 4 Jul 2013 17:02:59 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 55428E0966; Thu, 4 Jul 2013 17:02:51 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id BF5FEE0966 for ; Thu, 4 Jul 2013 17:02:50 +0000 (UTC) Received: from hornbill.gentoo.org (hornbill.gentoo.org [94.100.119.163]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 68A8833E897 for ; Thu, 4 Jul 2013 17:02:49 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by hornbill.gentoo.org (Postfix) with ESMTP id 056FAE5460 for ; Thu, 4 Jul 2013 17:02:48 +0000 (UTC) From: "André Erdmann" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "André Erdmann" Message-ID: <1372956831.3187207842e4c7022f3129bf5343924a1e33b594.dywi@gentoo> Subject: [gentoo-commits] proj/R_overlay:gsoc13/next commit in: roverlay/ebuild/, roverlay/depres/simpledeprule/, roverlay/depres/, roverlay/, ... X-VCS-Repository: proj/R_overlay X-VCS-Files: roverlay/depres/channels.py roverlay/depres/depenv.py roverlay/depres/depresolver.py roverlay/depres/depresult.py roverlay/depres/deprule.py roverlay/depres/deptype.py roverlay/depres/listeners.py roverlay/depres/simpledeprule/abstractrules.py roverlay/depres/simpledeprule/console.py roverlay/depres/simpledeprule/rulemaker.py roverlay/ebuild/creation.py roverlay/ebuild/depfilter.py roverlay/ebuild/depres.py roverlay/ebuild/ebuilder.py roverlay/overlay/category.py roverlay/overlay/pkgdir/packagedir_base.py roverlay/overlay/root.py roverlay/packageinfo.py X-VCS-Directories: roverlay/ebuild/ roverlay/depres/simpledeprule/ roverlay/depres/ roverlay/ roverlay/overlay/pkgdir/ roverlay/overlay/ X-VCS-Committer: dywi X-VCS-Committer-Name: André Erdmann X-VCS-Revision: 3187207842e4c7022f3129bf5343924a1e33b594 X-VCS-Branch: gsoc13/next Date: Thu, 4 Jul 2013 17:02:48 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: b7453140-65e1-4364-8362-d96b4ab568ee X-Archives-Hash: 0982d52432bb6189a727a3e99bfbd308 commit: 3187207842e4c7022f3129bf5343924a1e33b594 Author: André Erdmann mailerd de> AuthorDate: Thu Jul 4 16:53:51 2013 +0000 Commit: André Erdmann mailerd de> CommitDate: Thu Jul 4 16:53:51 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=31872078 roverlay/depres: return depresult objects Dependency resolution now creates depresult objects if a rule matches a dependency string. Previously, it returned a 2-tuple (score,resolving_dep). This is "necessary" for selfdep reduction (also see pseudocode in overlay/root.py). Additionally, this commit allows to define selfdeps that don't belong to the default category ("sci-R") in rule files, either by OR-ing a deptype with "#deptype ,selfdep" or by prefixing a single rule with "@selfdep". The rule stub syntax ("zoo") is mostly unaffected by this change, but roverlay is able to detect (non-)default-category selfdeps now. --- roverlay/depres/channels.py | 8 +- roverlay/depres/depenv.py | 3 + roverlay/depres/depresolver.py | 16 +-- roverlay/depres/depresult.py | 131 +++++++++++++++++++++++++ roverlay/depres/deprule.py | 23 ++++- roverlay/depres/deptype.py | 12 ++- roverlay/depres/listeners.py | 2 +- roverlay/depres/simpledeprule/abstractrules.py | 50 +++++----- roverlay/depres/simpledeprule/console.py | 4 +- roverlay/depres/simpledeprule/rulemaker.py | 76 ++++++++------ roverlay/ebuild/creation.py | 14 +-- roverlay/ebuild/depfilter.py | 4 +- roverlay/ebuild/depres.py | 118 +++++++++++++--------- roverlay/ebuild/ebuilder.py | 6 +- roverlay/overlay/category.py | 2 +- roverlay/overlay/pkgdir/packagedir_base.py | 4 + roverlay/overlay/root.py | 34 +++++++ roverlay/packageinfo.py | 25 +++-- 18 files changed, 399 insertions(+), 133 deletions(-) diff --git a/roverlay/depres/channels.py b/roverlay/depres/channels.py index 3e0085f..b1f39bd 100644 --- a/roverlay/depres/channels.py +++ b/roverlay/depres/channels.py @@ -14,6 +14,8 @@ __all__ = [ 'EbuildJobChannel', ] import logging +import roverlay.depres.depresult + from roverlay.depres import deptype from roverlay.depres.depenv import DepEnv from roverlay.depres.communication import DependencyResolverChannel @@ -192,9 +194,9 @@ class EbuildJobChannel ( _EbuildJobChannelBase ): ret = True elif deptype.mandatory & ~dep_env.deptype_mask: # not resolved, but deptype has no mandatory bit set - # => dep is not required, resolve as None and add it to - # the list of unresolvable deps - resolved ( None ) + # => dep is not required, resolve it as "not resolved" + # and add it to the list of unresolvable deps + resolved ( roverlay.depres.depresult.DEP_NOT_RESOLVED ) unresolvable ( dep_env.dep_str ) ret = True # else failed diff --git a/roverlay/depres/depenv.py b/roverlay/depres/depenv.py index c298447..3d324a4 100644 --- a/roverlay/depres/depenv.py +++ b/roverlay/depres/depenv.py @@ -191,6 +191,7 @@ class DepEnv ( object ): result.append ( dict ( name = m.group ( 'name' ), + name_low = m.group ( 'name' ).lower(), version_modifier = vmod, version = version, version_strlist = version_strlist, @@ -267,3 +268,5 @@ class DepEnv ( object ): def get_resolved ( self ): return self.resolved_by # --- end of get_resolved (...) --- + +# --- end of DepEnv --- diff --git a/roverlay/depres/depresolver.py b/roverlay/depres/depresolver.py index b9465fd..a49697a 100644 --- a/roverlay/depres/depresolver.py +++ b/roverlay/depres/depresolver.py @@ -177,7 +177,9 @@ class DependencyResolver ( object ): # log this event if event_type == events.DEPRES_EVENTS ['RESOLVED']: self.logger_resolved.info ( - "{!r} as {!r}".format ( dep_env.dep_str, dep_env.resolved_by ) + "{!r} as {!r}".format ( + dep_env.dep_str, dep_env.resolved_by.dep + ) ) elif event_type == events.DEPRES_EVENTS ['UNRESOLVABLE']: self.logger_unresolvable.info ( "{!r}".format ( dep_env.dep_str ) ) @@ -378,8 +380,8 @@ class DependencyResolver ( object ): if p.accepts ( dep_env.deptype_mask, try_other=False ) ): result = rulepool.matches ( dep_env ) - if result [0] > 0: - resolved = result [1] + if result: + resolved = result is_resolved = 2 break # TRY_OTHER searching is disabled for dynamic rule pools, @@ -391,8 +393,8 @@ class DependencyResolver ( object ): if p.accepts ( dep_env.deptype_mask, try_other=False ) ): result = rulepool.matches ( dep_env ) - if result [0] > 0: - resolved = result [1] + if result: + resolved = result is_resolved = 2 break @@ -405,8 +407,8 @@ class DependencyResolver ( object ): if p.accepts ( ~dep_env.deptype_mask, try_other=True ) ): result = rulepool.matches ( dep_env ) - if result [0] > 0: - resolved = result [1] + if result: + resolved = result is_resolved = 2 break # -- diff --git a/roverlay/depres/depresult.py b/roverlay/depres/depresult.py new file mode 100644 index 0000000..91a4688 --- /dev/null +++ b/roverlay/depres/depresult.py @@ -0,0 +1,131 @@ +# R overlay -- dependency resolution, depres result +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +__all__ = [ 'DepResult', 'DEP_NOT_RESOLVED', ] + +import roverlay.depres.depenv + +EMPTY_STR = "" + +class DepResult ( object ): + + # TODO/FIXME: define those constants somewhere else + # (especially VMOD_??) + # + VMOD_COMPARE_MAP = { + # p.version_compare_* functions are TODO + roverlay.depres.depenv.DepEnv.VMOD_EQ: ( + lambda p: p.version_compare_eq + ), + roverlay.depres.depenv.DepEnv.VMOD_NE: ( + lambda p: p.version_compare_ne + ), + roverlay.depres.depenv.DepEnv.VMOD_GT: ( + lambda p: p.version_compare_gt + ), + roverlay.depres.depenv.DepEnv.VMOD_GE: ( + lambda p: p.version_compare_ge + ), + roverlay.depres.depenv.DepEnv.VMOD_LT: ( + lambda p: p.version_compare_lt + ), + roverlay.depres.depenv.DepEnv.VMOD_LE: ( + lambda p: p.version_compare_le + ), + } + + def __init__ ( self, + dep, score, matching_rule, dep_env=None, fuzzy=None + ): + self.dep = dep + self.score = score + #assert hasattr ( matching_rule, 'is_selfdep' ) + self.is_selfdep = matching_rule.is_selfdep if matching_rule else 0 + + if self.is_selfdep: + self.fuzzy = fuzzy + self.resolving_package = matching_rule.resolving_package + # --- end of DepResult --- + + def __eq__ ( self, other ): + if isinstance ( other, str ): + return str ( self ) == other + elif isinstance ( other, DepResult ): + return ( + self.score == other.score + and self.is_selfdep == other.is_selfdep + and self.dep == other.dep + ) + else: + return NotImplemented + # --- end of __eq__ (...) --- + + def __hash__ ( self ): + #return hash ( self.__dict__.values() ) + return hash (( self.dep, self.score, self.is_selfdep )) + # --- end of __hash__ (...) --- + + def __bool__ ( self ): + return self.score > 0 + #and self.dep is not False + # --- end of __bool__ (...) --- + + def __str__ ( self ): + return self.dep if self.dep is not None else EMPTY_STR + # --- end of __str__ (...) --- + + def __getitem__ ( self, key ): + # for backwards compatibility, indexing is supported + if key == 0: + return self.score + elif key == 1: + return self.dep + elif isinstance ( key, int ): + raise IndexError ( key ) + else: + raise KeyError ( key ) + # --- end of __getitem__ (...) --- + + def prepare_selfdep_reduction ( self ): + cat, sepa, pkg = self.resolving_package.rpartition ( '/' ) + self.category = cat + self.package = pkg + self.candidates = list() + self.link = self.candidates.append + + if self.fuzzy: + vmod = self.fuzzy ['vmod'] + self.version = self.fuzzy ['version_tuple'] + self._version_compare = self.VMOD_COMPARE_MAP [vmod] + self.version_compare = ( + lambda p: self._version_compare ( p ) ( self.version ) + ) + # --- end of prepare_selfdep_reduction (...) --- + + def deps_satisfiable ( self ): + return bool ( self.candidates ) + + def link_if_version_matches ( self, p ): + if not self.fuzzy or self.version_compare ( p ): + self.link ( p ) + return True + else: + return False + # --- end of link_if_version_matches (...) --- + + def reduce ( self ): + candidates = list ( + p for p in self.candidates if p.has_valid_selfdeps() + ) + num_removed = len ( self.candidates ) - len ( candidates ) + if num_removed != 0: + self.candidates = candidates + return num_removed + # --- end of reduce (...) --- + +# --- end of DepResult --- + +DEP_NOT_RESOLVED = DepResult ( dep=None, score=-2, matching_rule=None ) diff --git a/roverlay/depres/deprule.py b/roverlay/depres/deprule.py index 46c710d..1d36941 100644 --- a/roverlay/depres/deprule.py +++ b/roverlay/depres/deprule.py @@ -8,6 +8,8 @@ __all__ = [ 'DependencyRule', 'DependencyRulePool', ] +import roverlay.depres.depresult + from roverlay.depres import deptype class DependencyRule ( object ): @@ -25,11 +27,19 @@ class DependencyRule ( object ): # --- end of __init__ (...) --- def matches ( self, dep_env ): - """Returns a tuple ( score ::= int > 0, matching dep ::= str ) + """Returns a tuple ( score ::= int > 0, matching dep ::= DepResult ) if this rule matches the given DepEnv, else None""" return None # --- end of matches (...) --- + def _make_result ( self, resolved_dep, score, *args, **kw ): + return roverlay.depres.depresult.DepResult ( + resolved_dep, score, self, *args, **kw + ) + # --- end of _make_result (...) --- + + make_result = _make_result + # --- end of DependencyRule --- @@ -114,6 +124,11 @@ class DependencyRulePool ( object ): # cannot expect a result in this case - abort now pass + elif skip_matches == 0: + for rule in self.rules: + result = rule.matches ( dep_env ) + if result: + return result else: skipped = 0 # python3 requires list ( range ... ) @@ -125,12 +140,12 @@ class DependencyRulePool ( object ): for index in order: result = self.rules [index].matches ( dep_env ) - if result is not None and result [0] > 0: + if result: if skipped < skip_matches: skipped += 1 else: return result - - return ( -1, None ) + # default return + return None # --- end of matches (...) --- diff --git a/roverlay/depres/deptype.py b/roverlay/depres/deptype.py index 269f666..f430316 100644 --- a/roverlay/depres/deptype.py +++ b/roverlay/depres/deptype.py @@ -13,14 +13,22 @@ # ::= 2**k | k in {0,1,2,...} # try_other indicates that the dep can be checked world-wide (in non-accepting # rule pools) after unsuccessful resolution +# try_other = 1 mandatory = 2 external = 4 internal = 8 +#internal does not imply selfdep +# internal := dependency on package +# selfdep := created overlay has dependency +selfdep = 16 -_MAX = 15 +_MAX = 31 -ALL = external | internal | mandatory +#VIRTUAL = try_other | mandatory | selfdep + +NONE = 0 +ALL = external | internal | mandatory RESOLVE_ALL = external | internal SYS = external | mandatory diff --git a/roverlay/depres/listeners.py b/roverlay/depres/listeners.py index 29dd539..3d5ceaf 100644 --- a/roverlay/depres/listeners.py +++ b/roverlay/depres/listeners.py @@ -111,7 +111,7 @@ class ResolvedFileListener ( FileListener ): def notify ( self, event_type, dep_env=None, pkg_env=None, **extra ): self._event ( event_type, "{dep_str!r} as {dep!r}".format ( - dep_str=dep_env.dep_str, dep=dep_env.resolved_by + dep_str=dep_env.dep_str, dep=dep_env.resolved_by.dep ) ) # --- end of notify (...) --- diff --git a/roverlay/depres/simpledeprule/abstractrules.py b/roverlay/depres/simpledeprule/abstractrules.py index 0713c81..b80eb00 100644 --- a/roverlay/depres/simpledeprule/abstractrules.py +++ b/roverlay/depres/simpledeprule/abstractrules.py @@ -20,7 +20,7 @@ class SimpleRule ( deprule.DependencyRule ): def __init__ ( self, dep_str=None, priority=50, resolving_package=None, - is_selfdep=False, logger_name='simple_rule' + is_selfdep=0, logger_name='simple_rule' ): """Initializes a SimpleIgnoreDependencyRule. @@ -29,25 +29,22 @@ class SimpleRule ( deprule.DependencyRule ): * priority -- priority of this rule """ super ( SimpleRule, self ) . __init__ ( priority ) - self.dep_alias = list() - - self.logger = TMP_LOGGER.getChild ( logger_name ) - - self.is_selfdep = is_selfdep - - self.resolving_package = resolving_package + self.dep_alias = list() + self.logger = TMP_LOGGER.getChild ( logger_name ) + self.is_selfdep = int ( is_selfdep or 0 ) + self.resolving_package = resolving_package self.prepare_lowercase_alias = True - if not dep_str is None: + if dep_str is not None: self.dep_alias.append ( dep_str ) - if self.is_selfdep and dep_str is not None: - # add the actual package name (replace '_' by '.') to self.dep_alias - actual_name = dep_str.replace ( '_', '.' ) - if actual_name != dep_str: - self.dep_alias.append ( dep_str ) - + if self.is_selfdep: + # add the actual package name (replace '_' by '.') + # to self.dep_alias + actual_name = dep_str.replace ( '_', '.' ) + if actual_name != dep_str: + self.dep_alias.append ( dep_str ) # --- end of __init__ (...) --- def done_reading ( self ): @@ -92,7 +89,9 @@ class SimpleRule ( deprule.DependencyRule ): "matches {dep_str} with score {s} and priority {p}.".format ( dep_str=dep_env.dep_str, s=self.max_score, p=self.priority ) ) - return ( self.max_score, self.resolving_package ) + return self.make_result ( + self.resolving_package, self.max_score, dep_env=dep_env + ) return None # --- end of matches (...) --- @@ -116,7 +115,7 @@ class SimpleRule ( deprule.DependencyRule ): else: resolving = self.resolving_package - if self.is_selfdep: + if self.is_selfdep == 2: resolving = resolving.rpartition ( '/' ) [2] @@ -125,6 +124,8 @@ class SimpleRule ( deprule.DependencyRule ): # -- end if; if self.is_selfdep: + if self.is_selfdep == 1: + yield '@selfdep' yield resolving elif len ( self.dep_alias ) == 0: @@ -164,18 +165,18 @@ class FuzzySimpleRule ( SimpleRule ): elif self.resolving_package is None: # ignore rule for fuzzy in dep_env.fuzzy: - if self._find ( fuzzy ['name'], lowercase=True ): + if self._find ( fuzzy ['name_low'], lowercase=True ): return ( 1, fuzzy ) else: for fuzzy in dep_env.fuzzy: - if self._find ( fuzzy ['name'], lowercase=True ): + if self._find ( fuzzy ['name_low'], lowercase=True ): return ( 0, fuzzy ) # -- end if find (=match found) # -- end if return None # --- end of match_prepare (...) --- - def log_fuzzy_match ( self, dep_env, dep, score ): + def log_fuzzy_match ( self, dep_env, dep, score, fuzzy ): if dep is False: return None else: @@ -185,7 +186,7 @@ class FuzzySimpleRule ( SimpleRule ): dep_str=dep_env.dep_str, dep=dep, s=score ) ) - return ( score, dep ) + return self.make_result ( dep, score, dep_env=dep_env, fuzzy=fuzzy ) # --- end of log_fuzzy_match (...) --- def log_standard_match ( self, dep_env, score ): @@ -194,7 +195,7 @@ class FuzzySimpleRule ( SimpleRule ): dep_str=dep_env.dep_str, s=score, p=self.priority ) ) - return ( score, self.resolving_package ) + return self.make_result ( self.resolving_package, score ) # --- end of log_standard_match (...) --- def handle_version_relative_match ( self, dep_env, fuzzy ): @@ -213,12 +214,13 @@ class FuzzySimpleRule ( SimpleRule ): return self.log_fuzzy_match ( dep_env, self.handle_version_relative_match ( dep_env, fuzzy ), - score + score, + fuzzy ) elif match_type == 1: # name-only match (ignore rule?) return self.log_fuzzy_match ( - dep_env, self.resolving_package, score + dep_env, self.resolving_package, score, fuzzy ) else: # non-fuzzy match diff --git a/roverlay/depres/simpledeprule/console.py b/roverlay/depres/simpledeprule/console.py index e4e5efc..a845d41 100644 --- a/roverlay/depres/simpledeprule/console.py +++ b/roverlay/depres/simpledeprule/console.py @@ -74,14 +74,14 @@ class PackageDirRuleMaker ( object ): yield rules.SimpleFuzzyDependencyRule ( resolving_package = strutil.fix_ebuild_name ( cat + dep ), dep_str = dep, - is_selfdep=True + is_selfdep=2 ) else: for dep in self._scan ( distdir ): yield rules.SimpleDependencyRule ( resolving_package = strutil.fix_ebuild_name ( cat + dep ), dep_str = dep, - is_selfdep=True + is_selfdep=2 ) # --- end of make_rules (...) --- diff --git a/roverlay/depres/simpledeprule/rulemaker.py b/roverlay/depres/simpledeprule/rulemaker.py index a993341..ed39791 100644 --- a/roverlay/depres/simpledeprule/rulemaker.py +++ b/roverlay/depres/simpledeprule/rulemaker.py @@ -31,15 +31,24 @@ class SimpleRuleMaker ( object ): '\s+::\s+' if rule_separator is None else rule_separator ) + self.DEPTYPE_MAP = { + 'all' : deptype.ALL, + 'sys' : deptype.external, + 'pkg' : deptype.internal, + 'selfdep' : deptype.selfdep, + } + self.multiline_start = '{' self.multiline_stop = '}' self.comment_char = '#' + self.kw_selfdep_once = '@selfdep' self._kwmap = rules.RuleConstructor ( eapi = config.get_or_fail ( 'EBUILD.eapi' ) ) # deptype_kw is '#deptype' (this keyword requires comment 'mode') self.deptype_kw = 'deptype' self._deptype = deptype.ALL + self._deptype_once = deptype.NONE self._next = None # [ ( deptype, rule ), ... ] self._rules = list() @@ -72,19 +81,28 @@ class SimpleRuleMaker ( object ): return ret # --- end of done (...) --- - def _get_deptype ( self, t ): - if len ( t ) == 0 or t == 'all': - return deptype.ALL - elif t == 'sys': - return deptype.external - elif t == 'pkg': - return deptype.internal + def _parse_deptype ( self, deptype_str ): + ret = deptype.NONE + try: + for item in deptype_str.split ( ',' ): + ret |= self.DEPTYPE_MAP [item] + except KeyError: + raise Exception ( "unknown deptype {!r}".format ( deptype_str ) ) + + return ret if ret != deptype.NONE else deptype.ALL + # --- end of _parse_deptype (...) --- + + def _get_effective_deptype ( self, clear_temporary=True ): + if self._deptype_once is not deptype.NONE: + if clear_temporary: + ret = self._deptype_once + self._deptype_once = deptype.NONE + return ret + else: + self._deptype_once else: - try: - return int ( t ) - except ValueError: - return None - # --- end of _get_deptype (...) --- + return self._deptype + # --- end of _get_effective_deptype (...) --- def _single_line_rule ( self, dep, dep_str='' ): # single line rule, either selfdep, @@ -92,35 +110,38 @@ class SimpleRuleMaker ( object ): # or normal rule 'dev-lang/R :: R' # selfdeps are always single line statements (!) + rule_deptype = self._get_effective_deptype() rule_class, resolving, kwargs = self._kwmap.lookup ( dep ) if dep_str: # normal rule new_rule = rule_class ( - resolving_package=resolving, - dep_str=dep_str, - is_selfdep=False, + resolving_package = resolving, + dep_str = dep_str, + is_selfdep = ( + 1 if ( rule_deptype & deptype.selfdep ) else 0 + ), **kwargs ) elif resolving is not None: - # selfdep + # selfdep (rule stub) dep_str = resolving resolving = ( config.get_or_fail ( 'OVERLAY.category' ) + '/' + resolving ) new_rule = rule_class ( - resolving_package=resolving, - dep_str=dep_str, - is_selfdep=True, + resolving_package = resolving, + dep_str = dep_str, + is_selfdep = 2, **kwargs ) else: return False new_rule.done_reading() - self._rules.append ( ( self._deptype, new_rule ) ) + self._rules.append ( ( rule_deptype, new_rule ) ) return True # --- end of _single_line_rule (...) --- @@ -144,23 +165,22 @@ class SimpleRuleMaker ( object ): if line [ 1 : 1 + len ( self.deptype_kw ) ] == self.deptype_kw : # changing deptype ("#deptype ") dtype_str = line [ len ( self.deptype_kw ) + 2 : ].lstrip().lower() - dtype = self._get_deptype ( dtype_str ) - if dtype is not None: - self._deptype = dtype - else: - raise AssertionError ( - "Expected deptype, but got {!r}.".format ( dtype_str ) - ) + self._deptype = self._parse_deptype ( dtype_str ) + # else is a comment, # it's intented that multi line rules cannot contain comments return True + elif line == self.kw_selfdep_once: + self._deptype_once = self._deptype | deptype.selfdep + return True + elif len ( line ) > 1 and line [-1] == self.multiline_start: l = line [:-1].rstrip() rule_class, resolving, kwargs = self._kwmap.lookup ( l ) self._next = ( - self._deptype, + self._get_effective_deptype(), rule_class ( resolving_package=resolving, **kwargs ), ) return True diff --git a/roverlay/ebuild/creation.py b/roverlay/ebuild/creation.py index 9fda2b6..819616a 100644 --- a/roverlay/ebuild/creation.py +++ b/roverlay/ebuild/creation.py @@ -125,15 +125,16 @@ class EbuildCreation ( object ): ) return False - _dep_resolution = depres.EbuildDepRes ( + dep_resolution = depres.EbuildDepRes ( self.package_info, self.logger, run_now=True, depres_channel_spawner=self.depres_channel_spawner, err_queue=self.err_queue ) - if _dep_resolution.success(): - dep_result = _dep_resolution.get_result() + if dep_resolution.success(): + #dep_result = dep_resolution.get_result() ebuild = ebuilder.Ebuilder() + evars_dep = dep_resolution.get_evars() evars_extra = self.package_info.get_evars() if evars_extra: @@ -145,10 +146,10 @@ class EbuildCreation ( object ): # ... # add *DEPEND to the ebuild - ebuild.use ( *dep_result [1] ) + ebuild.use_list ( evars_dep ) # IUSE - if dep_result [2]: + if dep_resolution.has_suggests: rsuggests = ebuild.get ( 'R_SUGGESTS' ) self.use_expand_flag_names = set ( rsuggests.get_flag_names() ) ebuild.use ( evars.IUSE ( sorted ( rsuggests.get_flags() ) ) ) @@ -170,8 +171,9 @@ class EbuildCreation ( object ): self.package_info.update_now ( ebuild=ebuild_text, - depres_result=dep_result + has_suggests=dep_resolution.has_suggests, ) + self.package_info.selfdeps = dep_resolution.get_selfdeps() return True diff --git a/roverlay/ebuild/depfilter.py b/roverlay/ebuild/depfilter.py index a4c528a..4d59386 100644 --- a/roverlay/ebuild/depfilter.py +++ b/roverlay/ebuild/depfilter.py @@ -12,9 +12,11 @@ filter out redundant dependencies on dev-lang/R. __all__ = [ 'dep_allowed', ] -def dep_allowed ( dep ): +def dep_allowed ( dep_result ): """Filters out redundant dependencies on dev-lang/R.""" + dep = dep_result.dep + if not dep: return 0 elif dep[0] in '|(': diff --git a/roverlay/ebuild/depres.py b/roverlay/ebuild/depres.py index ac65e31..4f12400 100644 --- a/roverlay/ebuild/depres.py +++ b/roverlay/ebuild/depres.py @@ -19,8 +19,8 @@ from roverlay.ebuild import evars, depfilter FIELDS_TO_EVAR = { 'R_SUGGESTS' : ( 'Suggests', ), - 'DEPENDS' : ( 'Depends', 'Imports' ), - 'RDEPENDS' : ( 'LinkingTo', 'SystemRequirements' ), + 'DEPEND' : ( 'Depends', 'Imports' ), + 'RDEPEND' : ( 'LinkingTo', 'SystemRequirements' ), # ? : ( 'Enhances', ) } @@ -61,8 +61,8 @@ def create_use_expand_var ( *args, **kwargs ): EBUILDVARS = { 'R_SUGGESTS' : create_use_expand_var, - 'DEPENDS' : evars.DEPEND, - 'RDEPENDS' : evars.RDEPEND, + 'DEPEND' : evars.DEPEND, + 'RDEPEND' : evars.RDEPEND, } @@ -89,11 +89,10 @@ class EbuildDepRes ( object ): # > 0 busy/working; 0 == done,success; < 0 done,fail self.status = 1 - self.result = None + self.depmap = None + self.missingdeps = None self.has_suggests = None - self.err_queue = err_queue - self._channels = None if run_now: @@ -110,13 +109,15 @@ class EbuildDepRes ( object ): """Returns the result of dependency resolution, as tuple ( , , ) """ + raise NotImplementedError() return ( self.status, self.result, self.has_suggests ) # --- end of get_result (...) --- def resolve ( self ): """Try to make/get dependency resolution results. Returns None.""" try: - self.result = None + self.missingdeps = None + self.depmap = None self._init_channels() if self._wait_resolve(): @@ -126,7 +127,8 @@ class EbuildDepRes ( object ): # unresolvable self.logger.info ( "Cannot satisfy dependencies!" ) - self.result = None + self.missingdeps = None + self.depmap = None self.status = -5 finally: @@ -198,59 +200,81 @@ class EbuildDepRes ( object ): def _make_result ( self ): """Make evars using the depres result.""" - # RDEPEND -> , DEPEND -> , .. - _depmap = dict() - # two for dep_type, loops to safely determine the actual deps - # (e.g. whether to include R_SUGGESTS in RDEPEND) - + # => + depmap = dict() unresolvable_optional_deps = set() for dep_type, channel in self._channels.items(): channel_result = channel.collect_dependencies() - deplist = tuple ( filter ( - depfilter.dep_allowed, channel_result [0] ) - ) + deps = set ( filter ( depfilter.dep_allowed, channel_result[0] ) ) - if len ( deplist ) > 0: - self.logger.debug ( - "adding {deps} to {depvar}".format ( - deps=deplist, depvar=dep_type + if deps: + self.logger.debug ( "adding {deps} to {depvar}".format ( + deps=deps, depvar=dep_type ) ) - _depmap [dep_type] = deplist + depmap [dep_type] = deps # else: (effectively) no dependencies for dep_type - if channel_result [1] is not None: - unresolvable_optional_deps |= channel_result [1] + if channel_result[1]: + unresolvable_optional_deps |= channel_result[1] self._close_channels() - # empty R_SUGGESTS has been filtered out - self.has_suggests = bool ( 'R_SUGGESTS' in _depmap ) - - _result = list() - for dep_type, deplist in _depmap.items(): - # add dependencies in no_append/override mode - _result.append ( - EBUILDVARS [dep_type] ( - deplist, - using_suggests=self.has_suggests, - use_expand=True - ) - ) + # remove redundant deps (DEPEND in RDEPEND, RDEPEND,DEPEND in R_SUGGESTS) + if 'RDEPEND' in depmap and 'DEPEND' in depmap: + depmap ['RDEPEND'] -= depmap ['DEPEND'] + if not depmap ['RDEPEND']: + del depmap ['RDEPEND'] + + self.has_suggests = False + if 'R_SUGGESTS' in depmap: + if 'RDEPEND' in depmap: + depmap ['R_SUGGESTS'] -= depmap ['RDEPEND'] + if 'DEPEND' in depmap: + depmap ['R_SUGGESTS'] -= depmap ['DEPEND'] + + if depmap ['R_SUGGESTS']: + self.has_suggests = True + else: + del depmap ['R_SUGGESTS'] - # When using suggested dependencies, RDEPENDS is required even if empty - if self.has_suggests and 'RDEPENDS' not in _depmap: - _result.append ( - EBUILDVARS ['RDEPENDS'] ( + + self.depmap = depmap + if unresolvable_optional_deps: + self.missingdeps = unresolvable_optional_deps + # --- end of _make_result (...) --- + + def get_selfdeps ( self ): + def gen_selfdeps(): + for deps in self.depmap.values(): + for dep_result in deps: + if dep_result.is_selfdep: + yield dep_result + # --- end of gen_selfdeps (...) --- + + return frozenset ( gen_selfdeps() ) or None + # --- end of get_selfdeps (...) --- + + def get_evars ( self ): + depgen = lambda D : ( k.dep for k in D ) + + evar_list = list ( + EBUILDVARS [evar_name] ( + depgen ( deps ), + using_suggests=self.has_suggests, use_expand=True + ) for evar_name, deps in self.depmap.items() + ) + if self.has_suggests and 'RDEPEND' not in self.depmap: + evar_list.append ( + EBUILDVARS ['RDEPEND'] ( None, using_suggests=True, use_expand=True ) ) - if unresolvable_optional_deps: -# if not self.has_suggests: raise AssertionError() #? - _result.append ( - evars.MISSINGDEPS ( unresolvable_optional_deps, do_sort=True ) + if self.missingdeps: + evar_list.append ( + evars.MISSINGDEPS ( self.missingdeps, do_sort=True ) ) - self.result = tuple ( _result ) - # --- end of _make_result (...) --- + return evar_list + # --- end of get_evars (...) --- diff --git a/roverlay/ebuild/ebuilder.py b/roverlay/ebuild/ebuilder.py index 152cfc7..6806478 100644 --- a/roverlay/ebuild/ebuilder.py +++ b/roverlay/ebuild/ebuilder.py @@ -52,11 +52,15 @@ class Ebuilder ( object ): arguments: * *evar_list -- """ + self.use_list ( evar_list ) + # --- end of use (...) --- + + def use_list ( self, evar_list ): for e in evar_list: if e is not None: assert e.name not in self._evars self._evars [e.name] = e - # --- end of use (...) --- + # --- end of use_list (...) --- def has ( self, evar_name ): """Returns True if an evar with name evar_name exists. diff --git a/roverlay/overlay/category.py b/roverlay/overlay/category.py index aab8ff4..9c5c79f 100644 --- a/roverlay/overlay/category.py +++ b/roverlay/overlay/category.py @@ -160,7 +160,7 @@ class Category ( object ): yield dict ( dep_str = name, resolving_package = ( self.name + "/" + name ), - is_selfdep = is_default_category, + is_selfdep = 2 if is_default_category else 1, # prefer packages from the default category (really?) priority = 80 if is_default_category else 90, ) diff --git a/roverlay/overlay/pkgdir/packagedir_base.py b/roverlay/overlay/pkgdir/packagedir_base.py index 1f033a0..8b5b634 100644 --- a/roverlay/overlay/pkgdir/packagedir_base.py +++ b/roverlay/overlay/pkgdir/packagedir_base.py @@ -379,6 +379,10 @@ class PackageDirBase ( object ): self._need_metadata = True self.modified = True if self.runtime_incremental: + raise Exception ( + '--write-immediate is currently not available ' + '(blocked by selfdep reduction).' + ) with self._lock: return self.write_ebuilds ( overwrite=False ) else: diff --git a/roverlay/overlay/root.py b/roverlay/overlay/root.py index 943518a..a5e847c 100644 --- a/roverlay/overlay/root.py +++ b/roverlay/overlay/root.py @@ -671,6 +671,40 @@ class Overlay ( object ): ) # --- end of import_ebuilds (...) --- + def remove_bad_packages ( self ): + # "prepare" + # + ## collect: + ## find all p with selfdeps + ## p->selfdeps->prepare_selfdep_reduction() + ## add p->selfdeps to a list/listlike object S + ## + ## link: + ## foreach selfdep in S loop + ## find candidates in overlay and link them to selfdep + ## end loop + # + # "reduce" + # + ## num_removed <- 0 + ## first <- True + ## + ## while num_removed > 0 or first loop + ## first <- False + ## foreach selfdep in S loop + ## num_removed += selfdep.reduce() + ## end loop + ## end loop + ## + # + # "balance" [if ] + # + ## find all p with valid == False + ## drop p + ## + raise NotImplementedError ( "TODO" ) + # --- end of remove_bad_packages (...) --- + def scan ( self, **kw ): def scan_categories(): for x in os.listdir ( self.physical_location ): diff --git a/roverlay/packageinfo.py b/roverlay/packageinfo.py index b86d69d..bef9327 100644 --- a/roverlay/packageinfo.py +++ b/roverlay/packageinfo.py @@ -104,6 +104,7 @@ class PackageInfo ( object ): 'imported', 'physical_only', 'src_uri', + 'has_suggests', )) _UPDATE_KEYS_SIMPLE_INITIAL = frozenset (( 'package_filename', @@ -126,10 +127,13 @@ class PackageInfo ( object ): arguments: * **initial_info -- passed to update ( **kw ) """ - self._info = dict() - self.readonly = False + self._info = dict() + self.valid = True + self.readonly = False + self.logger = LOGGER + self.selfdeps = None + #self.overlay_package_ref = None - self.logger = LOGGER #self._evars = dict() #self._lazy_actions = list() #(or set(), but list preserves order for actions with the same condition) @@ -137,6 +141,17 @@ class PackageInfo ( object ): self.update ( **initial_info ) # --- end of __init__ (...) --- + def has_valid_selfdeps ( self ): + """Returns True if all selfdeps of this package are valid.""" + v = self.valid + if v is True and self.selfdeps is not None and not all ( + map ( lambda d: d.deps_satisfiable(), self.selfdeps ) + ): + self.valid = False + return False + return v + # --- end of has_valid_selfdeps (...) --- + def attach_lazy_action ( self, lazy_action ): """Attaches a lazy action. Unsafe operation (no locks will be acquired etc.). @@ -664,11 +679,9 @@ class PackageInfo ( object ): self._use_pvr ( value ) elif key == 'suggests': + # FIXME/TODO: remove self._info ['has_suggests'] = value - elif key == 'depres_result': - self._info ['has_suggests'] = value [2] - elif key == 'filepath': self._use_filepath ( value )