summaryrefslogtreecommitdiffstats
path: root/bitbake-dev/lib/bb/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake-dev/lib/bb/__init__.py')
-rw-r--r--bitbake-dev/lib/bb/__init__.py1134
1 files changed, 0 insertions, 1134 deletions
diff --git a/bitbake-dev/lib/bb/__init__.py b/bitbake-dev/lib/bb/__init__.py
deleted file mode 100644
index f2f8f656d8..0000000000
--- a/bitbake-dev/lib/bb/__init__.py
+++ /dev/null
@@ -1,1134 +0,0 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# BitBake Build System Python Library
5#
6# Copyright (C) 2003 Holger Schurig
7# Copyright (C) 2003, 2004 Chris Larson
8#
9# Based on Gentoo's portage.py.
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License version 2 as
13# published by the Free Software Foundation.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License along
21# with this program; if not, write to the Free Software Foundation, Inc.,
22# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24__version__ = "1.9.0"
25
26__all__ = [
27
28 "debug",
29 "note",
30 "error",
31 "fatal",
32
33 "mkdirhier",
34 "movefile",
35
36 "tokenize",
37 "evaluate",
38 "flatten",
39 "relparse",
40 "ververify",
41 "isjustname",
42 "isspecific",
43 "pkgsplit",
44 "catpkgsplit",
45 "vercmp",
46 "pkgcmp",
47 "dep_parenreduce",
48 "dep_opconvert",
49
50# fetch
51 "decodeurl",
52 "encodeurl",
53
54# modules
55 "parse",
56 "data",
57 "command",
58 "event",
59 "build",
60 "fetch",
61 "manifest",
62 "methodpool",
63 "cache",
64 "runqueue",
65 "taskdata",
66 "providers",
67 ]
68
69whitespace = '\t\n\x0b\x0c\r '
70lowercase = 'abcdefghijklmnopqrstuvwxyz'
71
72import sys, os, types, re, string, bb
73from bb import msg
74
75#projectdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
76projectdir = os.getcwd()
77
78if "BBDEBUG" in os.environ:
79 level = int(os.environ["BBDEBUG"])
80 if level:
81 bb.msg.set_debug_level(level)
82
83class VarExpandError(Exception):
84 pass
85
86class MalformedUrl(Exception):
87 """Exception raised when encountering an invalid url"""
88
89
90#######################################################################
91#######################################################################
92#
93# SECTION: Debug
94#
95# PURPOSE: little functions to make yourself known
96#
97#######################################################################
98#######################################################################
99
100def plain(*args):
101 bb.msg.warn(''.join(args))
102
103def debug(lvl, *args):
104 bb.msg.debug(lvl, None, ''.join(args))
105
106def note(*args):
107 bb.msg.note(1, None, ''.join(args))
108
109def warn(*args):
110 bb.msg.warn(1, None, ''.join(args))
111
112def error(*args):
113 bb.msg.error(None, ''.join(args))
114
115def fatal(*args):
116 bb.msg.fatal(None, ''.join(args))
117
118
119#######################################################################
120#######################################################################
121#
122# SECTION: File
123#
124# PURPOSE: Basic file and directory tree related functions
125#
126#######################################################################
127#######################################################################
128
129def mkdirhier(dir):
130 """Create a directory like 'mkdir -p', but does not complain if
131 directory already exists like os.makedirs
132 """
133
134 debug(3, "mkdirhier(%s)" % dir)
135 try:
136 os.makedirs(dir)
137 debug(2, "created " + dir)
138 except OSError, e:
139 if e.errno != 17: raise e
140
141
142#######################################################################
143
144import stat
145
146def movefile(src,dest,newmtime=None,sstat=None):
147 """Moves a file from src to dest, preserving all permissions and
148 attributes; mtime will be preserved even when moving across
149 filesystems. Returns true on success and false on failure. Move is
150 atomic.
151 """
152
153 #print "movefile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")"
154 try:
155 if not sstat:
156 sstat=os.lstat(src)
157 except Exception, e:
158 print "movefile: Stating source file failed...", e
159 return None
160
161 destexists=1
162 try:
163 dstat=os.lstat(dest)
164 except:
165 dstat=os.lstat(os.path.dirname(dest))
166 destexists=0
167
168 if destexists:
169 if stat.S_ISLNK(dstat[stat.ST_MODE]):
170 try:
171 os.unlink(dest)
172 destexists=0
173 except Exception, e:
174 pass
175
176 if stat.S_ISLNK(sstat[stat.ST_MODE]):
177 try:
178 target=os.readlink(src)
179 if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
180 os.unlink(dest)
181 os.symlink(target,dest)
182 #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
183 os.unlink(src)
184 return os.lstat(dest)
185 except Exception, e:
186 print "movefile: failed to properly create symlink:", dest, "->", target, e
187 return None
188
189 renamefailed=1
190 if sstat[stat.ST_DEV]==dstat[stat.ST_DEV]:
191 try:
192 ret=os.rename(src,dest)
193 renamefailed=0
194 except Exception, e:
195 import errno
196 if e[0]!=errno.EXDEV:
197 # Some random error.
198 print "movefile: Failed to move", src, "to", dest, e
199 return None
200 # Invalid cross-device-link 'bind' mounted or actually Cross-Device
201
202 if renamefailed:
203 didcopy=0
204 if stat.S_ISREG(sstat[stat.ST_MODE]):
205 try: # For safety copy then move it over.
206 shutil.copyfile(src,dest+"#new")
207 os.rename(dest+"#new",dest)
208 didcopy=1
209 except Exception, e:
210 print 'movefile: copy', src, '->', dest, 'failed.', e
211 return None
212 else:
213 #we don't yet handle special, so we need to fall back to /bin/mv
214 a=getstatusoutput("/bin/mv -f "+"'"+src+"' '"+dest+"'")
215 if a[0]!=0:
216 print "movefile: Failed to move special file:" + src + "' to '" + dest + "'", a
217 return None # failure
218 try:
219 if didcopy:
220 missingos.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
221 os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
222 os.unlink(src)
223 except Exception, e:
224 print "movefile: Failed to chown/chmod/unlink", dest, e
225 return None
226
227 if newmtime:
228 os.utime(dest,(newmtime,newmtime))
229 else:
230 os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
231 newmtime=sstat[stat.ST_MTIME]
232 return newmtime
233
234def copyfile(src,dest,newmtime=None,sstat=None):
235 """
236 Copies a file from src to dest, preserving all permissions and
237 attributes; mtime will be preserved even when moving across
238 filesystems. Returns true on success and false on failure.
239 """
240 import os, stat, shutil
241
242 #print "copyfile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")"
243 try:
244 if not sstat:
245 sstat=os.lstat(src)
246 except Exception, e:
247 print "copyfile: Stating source file failed...", e
248 return False
249
250 destexists=1
251 try:
252 dstat=os.lstat(dest)
253 except:
254 dstat=os.lstat(os.path.dirname(dest))
255 destexists=0
256
257 if destexists:
258 if stat.S_ISLNK(dstat[stat.ST_MODE]):
259 try:
260 os.unlink(dest)
261 destexists=0
262 except Exception, e:
263 pass
264
265 if stat.S_ISLNK(sstat[stat.ST_MODE]):
266 try:
267 target=os.readlink(src)
268 if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
269 os.unlink(dest)
270 os.symlink(target,dest)
271 #os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
272 return os.lstat(dest)
273 except Exception, e:
274 print "copyfile: failed to properly create symlink:", dest, "->", target, e
275 return False
276
277 if stat.S_ISREG(sstat[stat.ST_MODE]):
278 try: # For safety copy then move it over.
279 shutil.copyfile(src,dest+"#new")
280 os.rename(dest+"#new",dest)
281 except Exception, e:
282 print 'copyfile: copy', src, '->', dest, 'failed.', e
283 return False
284 else:
285 #we don't yet handle special, so we need to fall back to /bin/mv
286 a=getstatusoutput("/bin/cp -f "+"'"+src+"' '"+dest+"'")
287 if a[0]!=0:
288 print "copyfile: Failed to copy special file:" + src + "' to '" + dest + "'", a
289 return False # failure
290 try:
291 os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
292 os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
293 except Exception, e:
294 print "copyfile: Failed to chown/chmod/unlink", dest, e
295 return False
296
297 if newmtime:
298 os.utime(dest,(newmtime,newmtime))
299 else:
300 os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
301 newmtime=sstat[stat.ST_MTIME]
302 return newmtime
303
304#######################################################################
305#######################################################################
306#
307# SECTION: Download
308#
309# PURPOSE: Download via HTTP, FTP, CVS, BITKEEPER, handling of MD5-signatures
310# and mirrors
311#
312#######################################################################
313#######################################################################
314
315def decodeurl(url):
316 """Decodes an URL into the tokens (scheme, network location, path,
317 user, password, parameters).
318
319 >>> decodeurl("http://www.google.com/index.html")
320 ('http', 'www.google.com', '/index.html', '', '', {})
321
322 CVS url with username, host and cvsroot. The cvs module to check out is in the
323 parameters:
324
325 >>> decodeurl("cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg")
326 ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'})
327
328 Dito, but this time the username has a password part. And we also request a special tag
329 to check out.
330
331 >>> decodeurl("cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81")
332 ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'})
333 """
334
335 m = re.compile('(?P<type>[^:]*)://((?P<user>.+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
336 if not m:
337 raise MalformedUrl(url)
338
339 type = m.group('type')
340 location = m.group('location')
341 if not location:
342 raise MalformedUrl(url)
343 user = m.group('user')
344 parm = m.group('parm')
345
346 locidx = location.find('/')
347 if locidx != -1:
348 host = location[:locidx]
349 path = location[locidx:]
350 else:
351 host = ""
352 path = location
353 if user:
354 m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
355 if m:
356 user = m.group('user')
357 pswd = m.group('pswd')
358 else:
359 user = ''
360 pswd = ''
361
362 p = {}
363 if parm:
364 for s in parm.split(';'):
365 s1,s2 = s.split('=')
366 p[s1] = s2
367
368 return (type, host, path, user, pswd, p)
369
370#######################################################################
371
372def encodeurl(decoded):
373 """Encodes a URL from tokens (scheme, network location, path,
374 user, password, parameters).
375
376 >>> encodeurl(['http', 'www.google.com', '/index.html', '', '', {}])
377 'http://www.google.com/index.html'
378
379 CVS with username, host and cvsroot. The cvs module to check out is in the
380 parameters:
381
382 >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}])
383 'cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg'
384
385 Dito, but this time the username has a password part. And we also request a special tag
386 to check out.
387
388 >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}])
389 'cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg'
390 """
391
392 (type, host, path, user, pswd, p) = decoded
393
394 if not type or not path:
395 fatal("invalid or missing parameters for url encoding")
396 url = '%s://' % type
397 if user:
398 url += "%s" % user
399 if pswd:
400 url += ":%s" % pswd
401 url += "@"
402 if host:
403 url += "%s" % host
404 url += "%s" % path
405 if p:
406 for parm in p.keys():
407 url += ";%s=%s" % (parm, p[parm])
408
409 return url
410
411#######################################################################
412
413def which(path, item, direction = 0):
414 """
415 Locate a file in a PATH
416 """
417
418 paths = (path or "").split(':')
419 if direction != 0:
420 paths.reverse()
421
422 for p in (path or "").split(':'):
423 next = os.path.join(p, item)
424 if os.path.exists(next):
425 return next
426
427 return ""
428
429#######################################################################
430
431
432
433
434#######################################################################
435#######################################################################
436#
437# SECTION: Dependency
438#
439# PURPOSE: Compare build & run dependencies
440#
441#######################################################################
442#######################################################################
443
444def tokenize(mystring):
445 """Breaks a string like 'foo? (bar) oni? (blah (blah))' into (possibly embedded) lists:
446
447 >>> tokenize("x")
448 ['x']
449 >>> tokenize("x y")
450 ['x', 'y']
451 >>> tokenize("(x y)")
452 [['x', 'y']]
453 >>> tokenize("(x y) b c")
454 [['x', 'y'], 'b', 'c']
455 >>> tokenize("foo? (bar) oni? (blah (blah))")
456 ['foo?', ['bar'], 'oni?', ['blah', ['blah']]]
457 >>> tokenize("sys-apps/linux-headers nls? (sys-devel/gettext)")
458 ['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']]
459 """
460
461 newtokens = []
462 curlist = newtokens
463 prevlists = []
464 level = 0
465 accum = ""
466 for x in mystring:
467 if x=="(":
468 if accum:
469 curlist.append(accum)
470 accum=""
471 prevlists.append(curlist)
472 curlist=[]
473 level=level+1
474 elif x==")":
475 if accum:
476 curlist.append(accum)
477 accum=""
478 if level==0:
479 print "!!! tokenizer: Unmatched left parenthesis in:\n'"+mystring+"'"
480 return None
481 newlist=curlist
482 curlist=prevlists.pop()
483 curlist.append(newlist)
484 level=level-1
485 elif x in whitespace:
486 if accum:
487 curlist.append(accum)
488 accum=""
489 else:
490 accum=accum+x
491 if accum:
492 curlist.append(accum)
493 if (level!=0):
494 print "!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+mystring+"'"
495 return None
496 return newtokens
497
498
499#######################################################################
500
501def evaluate(tokens,mydefines,allon=0):
502 """Removes tokens based on whether conditional definitions exist or not.
503 Recognizes !
504
505 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {})
506 ['sys-apps/linux-headers']
507
508 Negate the flag:
509
510 >>> evaluate(['sys-apps/linux-headers', '!nls?', ['sys-devel/gettext']], {})
511 ['sys-apps/linux-headers', ['sys-devel/gettext']]
512
513 Define 'nls':
514
515 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {"nls":1})
516 ['sys-apps/linux-headers', ['sys-devel/gettext']]
517
518 Turn allon on:
519
520 >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}, True)
521 ['sys-apps/linux-headers', ['sys-devel/gettext']]
522 """
523
524 if tokens == None:
525 return None
526 mytokens = tokens + [] # this copies the list
527 pos = 0
528 while pos < len(mytokens):
529 if type(mytokens[pos]) == types.ListType:
530 evaluate(mytokens[pos], mydefines)
531 if not len(mytokens[pos]):
532 del mytokens[pos]
533 continue
534 elif mytokens[pos][-1] == "?":
535 cur = mytokens[pos][:-1]
536 del mytokens[pos]
537 if allon:
538 if cur[0] == "!":
539 del mytokens[pos]
540 else:
541 if cur[0] == "!":
542 if (cur[1:] in mydefines) and (pos < len(mytokens)):
543 del mytokens[pos]
544 continue
545 elif (cur not in mydefines) and (pos < len(mytokens)):
546 del mytokens[pos]
547 continue
548 pos = pos + 1
549 return mytokens
550
551
552#######################################################################
553
554def flatten(mytokens):
555 """Converts nested arrays into a flat arrays:
556
557 >>> flatten([1,[2,3]])
558 [1, 2, 3]
559 >>> flatten(['sys-apps/linux-headers', ['sys-devel/gettext']])
560 ['sys-apps/linux-headers', 'sys-devel/gettext']
561 """
562
563 newlist=[]
564 for x in mytokens:
565 if type(x)==types.ListType:
566 newlist.extend(flatten(x))
567 else:
568 newlist.append(x)
569 return newlist
570
571
572#######################################################################
573
574_package_weights_ = {"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1} # dicts are unordered
575_package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list
576
577def relparse(myver):
578 """Parses the last elements of a version number into a triplet, that can
579 later be compared:
580
581 >>> relparse('1.2_pre3')
582 [1.2, -2, 3.0]
583 >>> relparse('1.2b')
584 [1.2, 98, 0]
585 >>> relparse('1.2')
586 [1.2, 0, 0]
587 """
588
589 number = 0
590 p1 = 0
591 p2 = 0
592 mynewver = myver.split('_')
593 if len(mynewver)==2:
594 # an _package_weights_
595 number = float(mynewver[0])
596 match = 0
597 for x in _package_ends_:
598 elen = len(x)
599 if mynewver[1][:elen] == x:
600 match = 1
601 p1 = _package_weights_[x]
602 try:
603 p2 = float(mynewver[1][elen:])
604 except:
605 p2 = 0
606 break
607 if not match:
608 # normal number or number with letter at end
609 divider = len(myver)-1
610 if myver[divider:] not in "1234567890":
611 # letter at end
612 p1 = ord(myver[divider:])
613 number = float(myver[0:divider])
614 else:
615 number = float(myver)
616 else:
617 # normal number or number with letter at end
618 divider = len(myver)-1
619 if myver[divider:] not in "1234567890":
620 #letter at end
621 p1 = ord(myver[divider:])
622 number = float(myver[0:divider])
623 else:
624 number = float(myver)
625 return [number,p1,p2]
626
627
628#######################################################################
629
630__ververify_cache__ = {}
631
632def ververify(myorigval,silent=1):
633 """Returns 1 if given a valid version string, els 0. Valid versions are in the format
634
635 <v1>.<v2>...<vx>[a-z,_{_package_weights_}[vy]]
636
637 >>> ververify('2.4.20')
638 1
639 >>> ververify('2.4..20') # two dots
640 0
641 >>> ververify('2.x.20') # 'x' is not numeric
642 0
643 >>> ververify('2.4.20a')
644 1
645 >>> ververify('2.4.20cvs') # only one trailing letter
646 0
647 >>> ververify('1a')
648 1
649 >>> ververify('test_a') # no version at all
650 0
651 >>> ververify('2.4.20_beta1')
652 1
653 >>> ververify('2.4.20_beta')
654 1
655 >>> ververify('2.4.20_wrongext') # _wrongext is no valid trailer
656 0
657 """
658
659 # Lookup the cache first
660 try:
661 return __ververify_cache__[myorigval]
662 except KeyError:
663 pass
664
665 if len(myorigval) == 0:
666 if not silent:
667 error("package version is empty")
668 __ververify_cache__[myorigval] = 0
669 return 0
670 myval = myorigval.split('.')
671 if len(myval)==0:
672 if not silent:
673 error("package name has empty version string")
674 __ververify_cache__[myorigval] = 0
675 return 0
676 # all but the last version must be a numeric
677 for x in myval[:-1]:
678 if not len(x):
679 if not silent:
680 error("package version has two points in a row")
681 __ververify_cache__[myorigval] = 0
682 return 0
683 try:
684 foo = int(x)
685 except:
686 if not silent:
687 error("package version contains non-numeric '"+x+"'")
688 __ververify_cache__[myorigval] = 0
689 return 0
690 if not len(myval[-1]):
691 if not silent:
692 error("package version has trailing dot")
693 __ververify_cache__[myorigval] = 0
694 return 0
695 try:
696 foo = int(myval[-1])
697 __ververify_cache__[myorigval] = 1
698 return 1
699 except:
700 pass
701
702 # ok, our last component is not a plain number or blank, let's continue
703 if myval[-1][-1] in lowercase:
704 try:
705 foo = int(myval[-1][:-1])
706 return 1
707 __ververify_cache__[myorigval] = 1
708 # 1a, 2.0b, etc.
709 except:
710 pass
711 # ok, maybe we have a 1_alpha or 1_beta2; let's see
712 ep=string.split(myval[-1],"_")
713 if len(ep)!= 2:
714 if not silent:
715 error("package version has more than one letter at then end")
716 __ververify_cache__[myorigval] = 0
717 return 0
718 try:
719 foo = string.atoi(ep[0])
720 except:
721 # this needs to be numeric, i.e. the "1" in "1_alpha"
722 if not silent:
723 error("package version must have numeric part before the '_'")
724 __ververify_cache__[myorigval] = 0
725 return 0
726
727 for mye in _package_ends_:
728 if ep[1][0:len(mye)] == mye:
729 if len(mye) == len(ep[1]):
730 # no trailing numeric is ok
731 __ververify_cache__[myorigval] = 1
732 return 1
733 else:
734 try:
735 foo = string.atoi(ep[1][len(mye):])
736 __ververify_cache__[myorigval] = 1
737 return 1
738 except:
739 # if no _package_weights_ work, *then* we return 0
740 pass
741 if not silent:
742 error("package version extension after '_' is invalid")
743 __ververify_cache__[myorigval] = 0
744 return 0
745
746
747def isjustname(mypkg):
748 myparts = string.split(mypkg,'-')
749 for x in myparts:
750 if ververify(x):
751 return 0
752 return 1
753
754
755_isspecific_cache_={}
756
757def isspecific(mypkg):
758 "now supports packages with no category"
759 try:
760 return __isspecific_cache__[mypkg]
761 except:
762 pass
763
764 mysplit = string.split(mypkg,"/")
765 if not isjustname(mysplit[-1]):
766 __isspecific_cache__[mypkg] = 1
767 return 1
768 __isspecific_cache__[mypkg] = 0
769 return 0
770
771
772#######################################################################
773
774__pkgsplit_cache__={}
775
776def pkgsplit(mypkg, silent=1):
777
778 """This function can be used as a package verification function. If
779 it is a valid name, pkgsplit will return a list containing:
780 [pkgname, pkgversion(norev), pkgrev ].
781
782 >>> pkgsplit('')
783 >>> pkgsplit('x')
784 >>> pkgsplit('x-')
785 >>> pkgsplit('-1')
786 >>> pkgsplit('glibc-1.2-8.9-r7')
787 >>> pkgsplit('glibc-2.2.5-r7')
788 ['glibc', '2.2.5', 'r7']
789 >>> pkgsplit('foo-1.2-1')
790 >>> pkgsplit('Mesa-3.0')
791 ['Mesa', '3.0', 'r0']
792 """
793
794 try:
795 return __pkgsplit_cache__[mypkg]
796 except KeyError:
797 pass
798
799 myparts = string.split(mypkg,'-')
800 if len(myparts) < 2:
801 if not silent:
802 error("package name without name or version part")
803 __pkgsplit_cache__[mypkg] = None
804 return None
805 for x in myparts:
806 if len(x) == 0:
807 if not silent:
808 error("package name with empty name or version part")
809 __pkgsplit_cache__[mypkg] = None
810 return None
811 # verify rev
812 revok = 0
813 myrev = myparts[-1]
814 ververify(myrev, silent)
815 if len(myrev) and myrev[0] == "r":
816 try:
817 string.atoi(myrev[1:])
818 revok = 1
819 except:
820 pass
821 if revok:
822 if ververify(myparts[-2]):
823 if len(myparts) == 2:
824 __pkgsplit_cache__[mypkg] = None
825 return None
826 else:
827 for x in myparts[:-2]:
828 if ververify(x):
829 __pkgsplit_cache__[mypkg]=None
830 return None
831 # names can't have versiony looking parts
832 myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]]
833 __pkgsplit_cache__[mypkg]=myval
834 return myval
835 else:
836 __pkgsplit_cache__[mypkg] = None
837 return None
838
839 elif ververify(myparts[-1],silent):
840 if len(myparts)==1:
841 if not silent:
842 print "!!! Name error in",mypkg+": missing name part."
843 __pkgsplit_cache__[mypkg]=None
844 return None
845 else:
846 for x in myparts[:-1]:
847 if ververify(x):
848 if not silent: error("package name has multiple version parts")
849 __pkgsplit_cache__[mypkg] = None
850 return None
851 myval = [string.join(myparts[:-1],"-"), myparts[-1],"r0"]
852 __pkgsplit_cache__[mypkg] = myval
853 return myval
854 else:
855 __pkgsplit_cache__[mypkg] = None
856 return None
857
858
859#######################################################################
860
861__catpkgsplit_cache__ = {}
862
863def catpkgsplit(mydata,silent=1):
864 """returns [cat, pkgname, version, rev ]
865
866 >>> catpkgsplit('sys-libs/glibc-1.2-r7')
867 ['sys-libs', 'glibc', '1.2', 'r7']
868 >>> catpkgsplit('glibc-1.2-r7')
869 [None, 'glibc', '1.2', 'r7']
870 """
871
872 try:
873 return __catpkgsplit_cache__[mydata]
874 except KeyError:
875 pass
876
877 cat = os.path.basename(os.path.dirname(mydata))
878 mydata = os.path.join(cat, os.path.basename(mydata))
879 if mydata[-3:] == '.bb':
880 mydata = mydata[:-3]
881
882 mysplit = mydata.split("/")
883 p_split = None
884 splitlen = len(mysplit)
885 if splitlen == 1:
886 retval = [None]
887 p_split = pkgsplit(mydata,silent)
888 else:
889 retval = [mysplit[splitlen - 2]]
890 p_split = pkgsplit(mysplit[splitlen - 1],silent)
891 if not p_split:
892 __catpkgsplit_cache__[mydata] = None
893 return None
894 retval.extend(p_split)
895 __catpkgsplit_cache__[mydata] = retval
896 return retval
897
898
899#######################################################################
900
901__vercmp_cache__ = {}
902
903def vercmp(val1,val2):
904 """This takes two version strings and returns an integer to tell you whether
905 the versions are the same, val1>val2 or val2>val1.
906
907 >>> vercmp('1', '2')
908 -1.0
909 >>> vercmp('2', '1')
910 1.0
911 >>> vercmp('1', '1.0')
912 0
913 >>> vercmp('1', '1.1')
914 -1.0
915 >>> vercmp('1.1', '1_p2')
916 1.0
917 """
918
919 # quick short-circuit
920 if val1 == val2:
921 return 0
922 valkey = val1+" "+val2
923
924 # cache lookup
925 try:
926 return __vercmp_cache__[valkey]
927 try:
928 return - __vercmp_cache__[val2+" "+val1]
929 except KeyError:
930 pass
931 except KeyError:
932 pass
933
934 # consider 1_p2 vc 1.1
935 # after expansion will become (1_p2,0) vc (1,1)
936 # then 1_p2 is compared with 1 before 0 is compared with 1
937 # to solve the bug we need to convert it to (1,0_p2)
938 # by splitting _prepart part and adding it back _after_expansion
939
940 val1_prepart = val2_prepart = ''
941 if val1.count('_'):
942 val1, val1_prepart = val1.split('_', 1)
943 if val2.count('_'):
944 val2, val2_prepart = val2.split('_', 1)
945
946 # replace '-' by '.'
947 # FIXME: Is it needed? can val1/2 contain '-'?
948
949 val1 = string.split(val1,'-')
950 if len(val1) == 2:
951 val1[0] = val1[0] +"."+ val1[1]
952 val2 = string.split(val2,'-')
953 if len(val2) == 2:
954 val2[0] = val2[0] +"."+ val2[1]
955
956 val1 = string.split(val1[0],'.')
957 val2 = string.split(val2[0],'.')
958
959 # add back decimal point so that .03 does not become "3" !
960 for x in range(1,len(val1)):
961 if val1[x][0] == '0' :
962 val1[x] = '.' + val1[x]
963 for x in range(1,len(val2)):
964 if val2[x][0] == '0' :
965 val2[x] = '.' + val2[x]
966
967 # extend varion numbers
968 if len(val2) < len(val1):
969 val2.extend(["0"]*(len(val1)-len(val2)))
970 elif len(val1) < len(val2):
971 val1.extend(["0"]*(len(val2)-len(val1)))
972
973 # add back _prepart tails
974 if val1_prepart:
975 val1[-1] += '_' + val1_prepart
976 if val2_prepart:
977 val2[-1] += '_' + val2_prepart
978 # The above code will extend version numbers out so they
979 # have the same number of digits.
980 for x in range(0,len(val1)):
981 cmp1 = relparse(val1[x])
982 cmp2 = relparse(val2[x])
983 for y in range(0,3):
984 myret = cmp1[y] - cmp2[y]
985 if myret != 0:
986 __vercmp_cache__[valkey] = myret
987 return myret
988 __vercmp_cache__[valkey] = 0
989 return 0
990
991
992#######################################################################
993
994def pkgcmp(pkg1,pkg2):
995 """ Compares two packages, which should have been split via
996 pkgsplit(). if the return value val is less than zero, then pkg2 is
997 newer than pkg1, zero if equal and positive if older.
998
999 >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r7'])
1000 0
1001 >>> pkgcmp(['glibc', '2.2.5', 'r4'], ['glibc', '2.2.5', 'r7'])
1002 -1
1003 >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r2'])
1004 1
1005 """
1006
1007 mycmp = vercmp(pkg1[1],pkg2[1])
1008 if mycmp > 0:
1009 return 1
1010 if mycmp < 0:
1011 return -1
1012 r1=string.atoi(pkg1[2][1:])
1013 r2=string.atoi(pkg2[2][1:])
1014 if r1 > r2:
1015 return 1
1016 if r2 > r1:
1017 return -1
1018 return 0
1019
1020
1021#######################################################################
1022
1023def dep_parenreduce(mysplit, mypos=0):
1024 """Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists:
1025
1026 >>> dep_parenreduce([''])
1027 ['']
1028 >>> dep_parenreduce(['1', '2', '3'])
1029 ['1', '2', '3']
1030 >>> dep_parenreduce(['1', '(', '2', '3', ')', '4'])
1031 ['1', ['2', '3'], '4']
1032 """
1033
1034 while mypos < len(mysplit):
1035 if mysplit[mypos] == "(":
1036 firstpos = mypos
1037 mypos = mypos + 1
1038 while mypos < len(mysplit):
1039 if mysplit[mypos] == ")":
1040 mysplit[firstpos:mypos+1] = [mysplit[firstpos+1:mypos]]
1041 mypos = firstpos
1042 break
1043 elif mysplit[mypos] == "(":
1044 # recurse
1045 mysplit = dep_parenreduce(mysplit,mypos)
1046 mypos = mypos + 1
1047 mypos = mypos + 1
1048 return mysplit
1049
1050
1051def dep_opconvert(mysplit, myuse):
1052 "Does dependency operator conversion"
1053
1054 mypos = 0
1055 newsplit = []
1056 while mypos < len(mysplit):
1057 if type(mysplit[mypos]) == types.ListType:
1058 newsplit.append(dep_opconvert(mysplit[mypos],myuse))
1059 mypos += 1
1060 elif mysplit[mypos] == ")":
1061 # mismatched paren, error
1062 return None
1063 elif mysplit[mypos]=="||":
1064 if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType):
1065 # || must be followed by paren'd list
1066 return None
1067 try:
1068 mynew = dep_opconvert(mysplit[mypos+1],myuse)
1069 except Exception, e:
1070 error("unable to satisfy OR dependancy: " + string.join(mysplit," || "))
1071 raise e
1072 mynew[0:0] = ["||"]
1073 newsplit.append(mynew)
1074 mypos += 2
1075 elif mysplit[mypos][-1] == "?":
1076 # use clause, i.e "gnome? ( foo bar )"
1077 # this is a quick and dirty hack so that repoman can enable all USE vars:
1078 if (len(myuse) == 1) and (myuse[0] == "*"):
1079 # enable it even if it's ! (for repoman) but kill it if it's
1080 # an arch variable that isn't for this arch. XXX Sparc64?
1081 if (mysplit[mypos][:-1] not in settings.usemask) or \
1082 (mysplit[mypos][:-1]==settings["ARCH"]):
1083 enabled=1
1084 else:
1085 enabled=0
1086 else:
1087 if mysplit[mypos][0] == "!":
1088 myusevar = mysplit[mypos][1:-1]
1089 enabled = not myusevar in myuse
1090 #if myusevar in myuse:
1091 # enabled = 0
1092 #else:
1093 # enabled = 1
1094 else:
1095 myusevar=mysplit[mypos][:-1]
1096 enabled = myusevar in myuse
1097 #if myusevar in myuse:
1098 # enabled=1
1099 #else:
1100 # enabled=0
1101 if (mypos +2 < len(mysplit)) and (mysplit[mypos+2] == ":"):
1102 # colon mode
1103 if enabled:
1104 # choose the first option
1105 if type(mysplit[mypos+1]) == types.ListType:
1106 newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
1107 else:
1108 newsplit.append(mysplit[mypos+1])
1109 else:
1110 # choose the alternate option
1111 if type(mysplit[mypos+1]) == types.ListType:
1112 newsplit.append(dep_opconvert(mysplit[mypos+3],myuse))
1113 else:
1114 newsplit.append(mysplit[mypos+3])
1115 mypos += 4
1116 else:
1117 # normal use mode
1118 if enabled:
1119 if type(mysplit[mypos+1]) == types.ListType:
1120 newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
1121 else:
1122 newsplit.append(mysplit[mypos+1])
1123 # otherwise, continue
1124 mypos += 2
1125 else:
1126 # normal item
1127 newsplit.append(mysplit[mypos])
1128 mypos += 1
1129 return newsplit
1130
1131if __name__ == "__main__":
1132 import doctest, bb
1133 bb.msg.set_debug_level(0)
1134 doctest.testmod(bb)