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