1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
# R overlay -- dep res channels
# Copyright 2006-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
import logging
from roverlay.depres.depenv import DepEnv
from roverlay.depres.communication import DependencyResolverChannel
COMLINK = logging.getLogger ( "depres.com" )
class EbuildJobChannel ( DependencyResolverChannel ):
"""The EbuildJobChannel is an interface to the dependency resolver used
in EbuildJobs.
It can be used to insert dependency strings, trigger dep resolution,
poll until done and collect the dependencies afterwards.
Note that this channel has a strict control flow:
add deps, then satisfy_request(): collect/lookup
"""
def __init__ ( self, name=None, logger=None ):
"""EbuildJobChannel
arguments:
* name -- name of this channel, optional
* logger --
"""
super ( EbuildJobChannel, self ) . __init__ ( main_resolver=None )
# this is the number of resolved deps so far, should only be modified
# in the join()-method
self._depdone = 0
# set of portage packages (resolved deps)
# this is None unless all deps have been successfully resolved
self._collected_deps = None
# used to communicate with the resolver
self._depres_queue = None
# the list of dependency lookups assigned to this channel
## todo: could remove this list
self.dep_env_list = list ()
_logger = logger if hasattr ( logger, 'log' ) else COMLINK
if name:
self.name = name
self.logger = _logger.getChild ( 'channel.' + name )
else:
self.logger = _logger.getChild ( 'channel' )
# --- end of __init__ (...) ---
def close ( self ):
if self._depdone >= 0:
# else already closed
super ( EbuildJobChannel, self ) . close()
del self.dep_env_list, self._collected_deps, self._depres_queue
del self.logger
self._depdone = -1
self._collected_deps = None
# --- end of close (...) ---
def set_resolver ( self, resolver, channel_queue, **extra ):
""" comment todo """
if self._depres_master is None:
self._depres_master = resolver
self._depres_queue = channel_queue
else:
raise Exception ( "channel already bound to a resolver." )
# --- end of set_resolver (...) ---
def add_dependency ( self, dep_str ):
"""Adds a dependency string that should be looked up.
This channel will create a "dependency environment" for it and enqueues
that in the dep resolver.
arguments:
* dep_str --
raises: Exception if this channel is done
returns: None (implicit)
"""
if self._depdone:
raise Exception (
"This channel is 'done', it doesn't accept new dependencies."
)
else:
dep_env = DepEnv ( dep_str )
self.dep_env_list.append ( dep_env )
self._depres_master.enqueue ( dep_env, self.ident )
# --- end of add_dependency (...) ---
def add_dependencies ( self, dep_list ):
"""Adds dependency strings to this channel. See add_dependency (...)
for details.
arguments:
* dep_list --
raises: passes Exception if this channel is done
returns: None (implicit)
"""
for dep_str in dep_list: self.add_dependency ( dep_str )
# --- end of add_dependencies (...) ---
def lookup ( self, dep_str ):
"""Looks up a specific dep str. Use the (faster) collect_dependencies()
method for getting all dependencies if order doesn't matter.
! It assumes that dep_str is unique in this channel.
arguments:
* dep_str -- to be looked up
raises: Exception if dep_str not in the dep env list.
"""
# it's no requirement that this channel is done when calling this method
for de in self.dep_env_list:
if de.dep_str == dep_str:
return de.get_result() [1]
raise Exception (
"bad usage: %s not in channel's dep env list!" % dep_str
)
# --- end of lookup (...) ---
def collect_dependencies ( self ):
"""Returns a list that contains all resolved deps,
including ignored deps that resolve to None.
You have to call satisfy_request(...) before using this method.
raises: Exception
"""
if not self._collected_deps is None:
self.logger.debug (
"returning collected deps: %s." % self._collected_deps
)
return self._collected_deps
raise Exception ( "cannot do that" )
# --- end of collect_dependencies (...) ---
def satisfy_request ( self, close_if_unresolvable=True ):
"""Tells to the dependency resolver to run.
It blocks until this channel is done, which means that either all
deps are resolved or one is unresolvable.
arguments:
* close_if_unresolvable -- close the channel if one dep is unresolvable
this seems reasonable and defaults to True
Returns the list of resolved dependencies if all could be resolved,
else None.
"""
# using a set allows easy difference() operations between
# DEPEND/RDEPEND/.. later, seewave requires sci-libs/fftw
# in both DEPEND and RDEPEND for example
dep_collected = set()
satisfiable = True
def handle_queue_item ( dep_env ):
self._depdone += 1
if dep_env.is_resolved():
### and dep_env in self.dep_env_list
# successfully resolved
dep_collected.add ( dep_env.get_result() [1] )
self._depres_queue.task_done()
return True
else:
# failed
self._depres_queue.task_done()
return False
# --- end of handle_queue_item (...) ---
while self._depdone < len ( self.dep_env_list ) and satisfiable:
# tell the resolver to start
self._depres_master.start()
# wait for one result at least
satisfiable = handle_queue_item ( self._depres_queue.get() )
while not self._depres_queue.empty() and satisfiable:
satisfiable = handle_queue_item ( self._depres_queue.get_nowait() )
# --- end while
if satisfiable:
self._collected_deps = dep_collected
return self._collected_deps
else:
if close_if_unresolvable: self.close()
return None
# --- end of join (...) ---
|