/[gentoo-src]/portage/pym/portage_core2.py
Gentoo

Contents of /portage/pym/portage_core2.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (show annotations) (download) (as text)
Wed Oct 29 16:19:40 2003 UTC (11 years, 1 month ago) by carpaski
Branch: MAIN
CVS Tags: HEAD
Changes since 1.5: +1 -1 lines
File MIME type: text/x-python
FILE REMOVED
These aren't used.

1 # Copyright 1999-200 Gentoo Technologies, Inc.
2 # Distributed under the terms of the GNU General Public License, v2 or later
3 # Author: Daniel Robbins <drobbins@gentoo.org>
4 # $Header: /home/cvsroot/gentoo-src/portage/pym/portage_core2.py,v 1.5 2002/06/17 00:47:06 drobbins Exp $
5
6 import string
7 import re
8 import os
9
10 endversion={"pre":-2,"p":1,"alpha":-4,"beta":-3,"rc":-1}
11
12 #The "categories" variable will eventually be moved out of portage_core2 and will
13 #most likely be read from a file.
14
15 categories=("app-i18n", "app-admin", "app-arch", "app-cdr", "app-crypt",
16 "app-doc", "app-editors", "app-emulation", "app-games", "app-misc",
17 "app-office", "app-shells", "app-text", "dev-db", "dev-java", "dev-lang",
18 "dev-libs", "dev-lisp", "dev-perl", "dev-python", "dev-ruby", "dev-util",
19 "gnome-base", "gnome-extra", "kde-apps", "kde-i18n", "kde-base", "kde-libs",
20 "media-gfx", "media-libs", "media-sound", "media-video", "net-analyzer",
21 "net-apache", "net-dialup", "net-fs", "net-ftp", "net-im", "net-irc",
22 "net-libs", "net-mail", "net-misc", "net-news", "net-nds", "net-print",
23 "net-www", "packages", "sys-apps", "sys-devel", "sys-kernel", "sys-libs",
24 "x11-base", "x11-libs", "x11-misc", "x11-terms", "x11-wm", "virtual",
25 "dev-tcltk")
26
27 class selector:
28
29 """The selector class is a generic parent class. Its child classes are
30 used to specify a certain subset of ebuilds/packages/db entries."""
31
32 #below, list all "public" attributes for this class
33 attributes=["repr","error","valid"]
34
35 def __init__(self):
36 pass
37
38 def invalidate(self,error=None):
39 "make this an invalid constraint"
40 if error:
41 if self.__dict__.has_key("repr"):
42 #record string representation for later reference
43 self.__dict__["error"]=self.repr+": "+error
44 else:
45 self.__dict__["error"]=error
46 else:
47 self.__dict__["error"]=None
48 self.__dict__["repr"]=None
49 for x in self.__class__.attributes:
50 self.__dict__[x]=None
51 self.__dict__["valid"]=0
52
53 def __nonzero__(self):
54 "This allows us to do an 'if myeid:'"
55 return self.valid
56
57 def __repr__(self):
58 return self.repr
59
60 def __str__(self):
61 return self.repr
62
63 class key(selector):
64 """A 'key' (name may change in the future) is used to specify a category
65 (in "not specific" mode) or a category and package (in "specific" mode).
66 It does not specify any version information."""
67
68 #used by selector.invalidate()
69 attributes=["category","package","specific"]
70
71 def __init__(self,myfoo):
72 #copy object
73 if type(myfoo)==type(self):
74 for x in selector.attributes+self.__class__.attributes:
75 self.__dict__[x]=myfoo.__dict__[x]
76 return
77 #new object
78 if not myfoo:
79 self.invalidate()
80 self.__dict__["repr"]=myfoo
81 mysplit=string.split(myfoo,"/")
82 if len(mysplit)>2:
83 self.invalidate()
84 return
85 self.__dict__["valid"]=1
86 if len(mysplit)==2:
87 self.__dict__["category"],self.__dict__["package"]=mysplit
88 self.__dict__["specific"]=1
89 else:
90 self.__dict__["category"]=myfoo
91 self.__dict__["package"]=None
92 self.__dict__["specific"]=0
93
94 def __setattr__(self,name,value):
95 if name not in key.attributes:
96 #ignore
97 return
98 self.__dict__[name]=value
99 if self.__dict__["package"]:
100 self.__dict__["specific"]=1
101 self.__dict__["repr"]=self.category+"/"+self.package
102 else:
103 self.__dict__["repr"]=self.category
104 self.__dict__["specific"]=0
105
106 class constraint(selector):
107
108 """generic parent class for eid and range classes. eids and ranges both
109 store version information, so we move a lot of the version functionality to
110 this parent class to eliminate redundant code and ease maintainance."""
111
112 attributes=["version","revision"]
113
114 evpat=re.compile(
115 '^(\d+)' # last version component
116 '([a-z])?' # letter component
117 '(_(?:alpha|beta|pre|rc|p)\d*)?' # suffix
118 )
119
120 evpat2=re.compile(
121 '_((?:alpha|beta|pre|rc|p))' # suffix
122 '(\d*)' # trailing digit
123 )
124
125 def __init__(self,myfoo):
126 if type(myfoo)==type(self):
127 #copy supplied object
128 for x in selector.attributes+self.attributes:
129 self.__dict__[x]=myfoo.__dict__[x]
130 #point to existing "cmp" comparison cache. This will allow us to share comparison caches as long
131 #as they are identical, and is a really nice way of saving space and being a bit more efficient.
132 self.__dict__["cmp"]=myfoo.__dict__["cmp"]
133 return
134
135 #generate a new eid from a supplied string.
136 if not myfoo:
137 self.invalidate()
138 return
139
140 self.__dict__["repr"]=myfoo
141 self.parse_repr()
142
143 def __cmp__(self,other):
144 "comparison operator code"
145 if self.cmp==None:
146 self.__dict__["cmp"]=self.gencmp()
147 if other.cmp==None:
148 other.__dict__["cmp"]=other.gencmp()
149 mycmp=self.cmp[:]
150 othercmp=other.cmp[:]
151 while(len(mycmp)<len(othercmp)):
152 mycmp.append([0,0,0,0])
153 while(len(mycmp)>len(othercmp)):
154 othercmp.append([0,0,0,0])
155 for x in range(0,len(mycmp)-1):
156 for y in range(0,4):
157 myret=mycmp[x][y]-othercmp[x][y]
158 if myret!=0:
159 return myret
160 return 0
161
162 def gencmp(self):
163 "internal function used to generate comparison lists"
164 cmplist=[]
165 splitversion=string.split(self.version,".")
166 for x in splitversion[:-1]:
167 cmplist.append([string.atoi(x),0,0,0])
168 print "DEBUG:",splitversion
169 match=self.__class__.evpat.search(splitversion[1])
170 if match:
171 ver, let, suf = match.groups()
172 ver=string.atoi(ver)
173 if let:
174 p1=ord(let)
175 else:
176 p1=0
177 if suf:
178 match2=self.__class__.evpat2.search(suf)
179 groups2 = match2.groups()
180 print "DEBUG2:",groups2
181 p2, p3 = groups2
182 p2=endversion[p2]
183 if not p3:
184 p3=0
185 else:
186 p3=string.atoi(p3)
187 else:
188 p2=p3=0
189 print "DEBUG FINAL:",ver,p1,p2,p3
190 cmplist.append([ver,p1,p2,p3])
191 cmplist.append([string.atoi(self.revision),0,0,0])
192 else:
193 raise TypeError
194 return cmplist
195
196 class eid(constraint):
197
198 """An eid is used to specify a single, specific category/package-version-rev."""
199
200 attributes=constraint.attributes+["key","category","version","revision"]
201
202 pattern=re.compile(
203 '^(\w+-\w+)/' # category
204 '([^/]+?)' # name
205 '-(\d+(?:\.\d+)*[a-z]*)' # version, eg 1.23.4a
206 '(_(?:alpha|beta|pre|rc|p)\d*)?' # special suffix
207 '(?:-r(\d+))?$') # revision, eg r12
208
209 def parse_repr(self):
210 match=self.__class__.pattern.search(self.repr)
211 if match:
212 (self.__dict__["category"], self.__dict__["package"], v1, v2, rev) = match.groups()
213 self.__dict__["version"] = v1 + (v2 or '')
214 self.__dict__["revision"] = rev or '0'
215 self.__dict__["valid"]=1
216 self.__dict__["cmp"]=None
217 self.__dict__["error"]=None
218 self.__dict__["key"]=self.category+"/"+self.package
219 return
220
221 # parse error -- try to figure out what's wrong with a looser regexp
222 match=re.compile(
223 '^(?:(.*)/)?' # category
224 '([^/]*?)' # name
225 '(?:-([^-_]*?))?' # version, eg 1.23.4a
226 '(_?(?:alpha|beta|pre|rc|p)\d*)?' # special suffix
227 '(?:-r(.*))?$' # revision, eg r12
228 ).search(myfoo)
229 if not match:
230 # no good -- even the loose regexp failed
231 self.invalidate("Unparseable")
232 return
233 (cat, pkg, v1, v2, rev) = match.groups()
234 self.__dict__["key"]=cat+"/"+pkg
235 # the loose regexp worked; now try to find which part is wrong
236 if not cat: # check category
237 self.invalidate("Missing category")
238 elif len(string.split(cat, "/")) > 1:
239 self.invalidate("More than on \"/\"")
240 elif len(string.split(cat, "-")) != 2:
241 self.invalidate("Expected exactly 1 \"-\" in category")
242 elif not pkg: # check package name
243 self.invalidate("Missing package name")
244 elif not v1: # check version
245 self.invalidate("Missing version")
246 elif not re.compile('^\d+(?:\.\d+)*[a-z]*$').search(v1):
247 self.invalidate("Invalid version number")
248 elif v2 and v2[0] != '_':
249 self.invalidate("Invalid ending version part")
250 elif rev != None and not re.compile('^\d+').search(rev):
251 self.invalidate("Invalid revision number")
252 else:
253 self.invalidate("Miscellaneous error")
254
255 def similar(self,other):
256 "are we talking about the same category and package (but possibly different versions/revs)?"
257 if (self.valid and other.valid) and (self.key == other.key):
258 return 1
259 return 0
260
261 def __setattr__(self,name,value):
262 """causes repr and key to be automatically regenerated if values are assigned to category, package, version, revision.
263 input is assumed to be valid. The comparison cache is also flushed (since data may not be up-to-date)"""
264 if not self.valid:
265 return
266 if name not in ["category","package","version","revision"]:
267 return
268 self.__dict__[name]=value
269 self.__dict__["key"]=self.category+"/"+self.package
270 self.__dict__["repr"]=self.key+"-"+self.version
271 if self.revision!="0":
272 self.__dict__["repr"]=self.repr+"-r"+self.revision
273 #reset cmp information (invalid)
274 self.__dict__["cmp"]=None
275
276 def __mod__(self,other):
277 "self is an eid, and other is a range"
278 return eval("self "+other.operator+" other")
279
280
281 class depid(constraint):
282
283 """A depid is used to specify a specific version/rev ("=") or simple
284 version range (">","<",">=","<="). Depids contain no category/package
285 information."""
286
287 #used by selector.invalidate()
288 attributes=["version","revision","operator"]
289
290 pattern=re.compile(
291 '(=|!|>|>=|<|<=)' # comparison operator
292 '(\d+(?:\.\d+)*[a-z]*)' # version, eg 1.23.4a
293 '(_(?:alpha|beta|pre|rc|p)\d*)?' # special suffix
294 '(?:-r(\d+))?$') # revision, eg r12
295
296 def parse_repr(self):
297 #we use self.__dict__ instead of direct assignment to avoid calling our __setattr__ method
298 match=self.__class__.pattern.search(self.repr)
299 if not match:
300 #we need to add error handling/detection here
301 self.invalidate("misc. error (full error messages not implemented yet)")
302 return
303 (op, v1, v2, rev) = match.groups()
304 self.__dict__["operator"] = op
305 self.__dict__["version"] = v1 + (v2 or '')
306 self.__dict__["revision"] = rev or '0'
307 self.__dict__["valid"]=1
308 self.__dict__["cmp"]=None
309 self.__dict__["error"]=None
310 return
311
312 def __setattr__(self,name,value):
313 """causes repr and key to be automatically regenerated if values are assigned. The comparison cache is also flushed (since data may not be up-to-date)"""
314 if not self.valid:
315 return
316 if name not in self.attributes:
317 #we can ignore the assign
318 return
319 self.__dict__[name]=value
320 self.__dict__["repr"]=self.operator+self.version
321 if self.revision!="0":
322 self.__dict__["repr"]=self.repr+"-r"+self.revision
323 #reset cmp information (since it's probably invalid now)
324 self.__dict__["cmp"]=None
325
326 class eidset:
327
328 """An eidset is used to encapsulate a bunch of eids, allowing subsets to be
329 queried using the subset() method. This particular eidset is hard-coded to
330 get its data from /usr/portage; the future eidset implementation will not
331 be tied to the filesystem, but there will likely be a portageeidset,
332 dbeidset, pkgeidset subclass that do pull their data from the filesystem,
333 just like this one."""
334
335 def __init__(self):
336 self.keydict={}
337
338 def populate(self):
339 os.chdir("/usr/portage")
340 for x in categories:
341 if not os.path.isdir(os.getcwd()+"/"+x):
342 continue
343 for y in os.listdir(os.getcwd()+"/"+x):
344 if not os.path.isdir(os.getcwd()+"/"+x+"/"+y):
345 continue
346 if y=="CVS":
347 continue
348 for mypkg in os.listdir(os.getcwd()+"/"+x+"/"+y):
349 if mypkg[-7:] != ".ebuild":
350 continue
351 mypkg=mypkg[:-7]
352 mykey=x+"/"+y
353 fullpkg=x+"/"+mypkg
354 if not self.keydict.has_key(mykey):
355 self.keydict[mykey]=[]
356 a=eid(fullpkg)
357 if not a.valid:
358 print "INVALID!",x,y,mypkg
359 continue
360 self.keydict[mykey].append(a)
361
362 def subset(self,selectors):
363 """A subset allows you to select a single category/package (key) and then whittle down the resultant set by using
364 depids."""
365 myset=eidset()
366 for x in selectors:
367 if x.__class__==key:
368 #if a key selector is specified, select a subset that matches that particular cat/pkg key
369 if x.specific:
370 if (not myset.keydict.has_key(x.repr)) and self.keydict.has_key(x.repr):
371 myset.keydict={}
372 #copy the list, don't just reference it (so we can modify it without introducing side-effects)
373 myset.keydict[x.repr]=self.keydict[x.repr][:]
374 else:
375 myset.keydict={}
376 else:
377 print "WARNING: non-specific key subsets not implemented yet."
378 #not specific... and not yet implemented! :)
379 pass
380 elif x.__class__==depid:
381 #if a constraint is specified, iterate through our keys and eliminate non-matching constraints
382 for mykey in myset.keydict.keys():
383 pos=0
384 mylist=myset.keydict[mykey]
385 #iterate through a list of constraints
386 while pos<len(mylist):
387 #is mylist's current package a member of the specified depid?
388 if not mylist[pos] % x:
389 del mylist[pos]
390 #don't increment, since we zapped the current node
391 continue
392 pos=pos+1
393 return myset
394
395 def _test():
396 import doctest, portage_core
397 return doctest.testmod(portage_core)
398
399 if __name__ == "__main__":
400 rng=[depid(">=3.0"),depid("<2.0"),depid(">3.1"),depid(">=3.1")]
401 e=[eid("sys-apps/foo-3.1"),eid("sys-apps/bar-2.0")]
402 print e
403 print rng
404 for x in e:
405 for y in rng:
406 # x % y prints out a boolean value that answers the question "is eid x a member of the range specified in depid y?"
407 # we override the modulo operator to allow for a membership test. We may move this to an .ismemberof() method in the
408 # future, which would be a trivial change and would be more self-documenting.
409 print `x`+`y`, x % y
410 #we create a new eidset
411 myset=eidset()
412 #we call the (hard-coded) populate function to fill the eidset with data from /usr/portage
413 myset.populate()
414 #we use the subset method to select the media-libs/libsdl packages >=1.2.0 but also <1.2.2.
415 mynewset=myset.subset([key("media-libs/libsdl"),depid(">=1.2.0"),depid("<1.2.2")])
416 #we print the result :)
417 print mynewset.keydict
418
419 """
420 The new download manager:
421
422 Karltk needs a feature (for Opera-6) where he can specify that a particular file should not be
423 mirrored on ibiblio, since it is illegal to do so. This only affects one package at the moment,
424 but could be more widely used in the future.
425
426 Karltk says:
427 manual:http://url
428
429 "error:generic error message"
430
431 Of course, we need to continue our ability to handle USE variables.
432
433 Support mirroring from a set of pre-defined mirrors:
434 mirror://gnu/path
435
436 We need to take advantage of filesizes in digests to detect partial downloads, and restart them.
437 We need a way of recording official mirrors (profiles/mirrors?) and selecting our preferred mirror
438 from the set (/etc/make.conf?)
439
440 Another thing that would be great to add is a way to auto-bind a shell variable
441 to a particular filename. Something like this:
442
443 A1~mirror://gnu/path
444 or
445 A1=mirror://gnu/path
446
447 Then, later in the build, you can do:
448
449 src_unpack() {
450 unpack $A1
451 }
452
453 This would be a totally *gorgeous* feature.
454
455 """

  ViewVC Help
Powered by ViewVC 1.1.20