summaryrefslogtreecommitdiff
path: root/bitbake/lib/bb/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/utils.py')
-rw-r--r--bitbake/lib/bb/utils.py396
1 files changed, 245 insertions, 151 deletions
diff --git a/bitbake/lib/bb/utils.py b/bitbake/lib/bb/utils.py
index 86b9c724e..c0cc9c6ea 100644
--- a/bitbake/lib/bb/utils.py
+++ b/bitbake/lib/bb/utils.py
@@ -19,10 +19,22 @@ BitBake Utility Functions
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+import re, fcntl, os, string, stat, shutil, time
+import sys
+import bb
+import errno
+import bb.msg
+from commands import getstatusoutput
+
+# Version comparison
separators = ".-"
-import re, fcntl, os, types, bb, string, stat, shutil
-from commands import getstatusoutput
+# Context used in better_exec, eval
+_context = {
+ "os": os,
+ "bb": bb,
+ "time": time,
+}
def explode_version(s):
r = []
@@ -60,9 +72,9 @@ def vercmp_part(a, b):
if ca == None and cb == None:
return 0
- if type(ca) is types.StringType:
+ if isinstance(ca, basestring):
sa = ca in separators
- if type(cb) is types.StringType:
+ if isinstance(cb, basestring):
sb = cb in separators
if sa and not sb:
return -1
@@ -85,6 +97,131 @@ def vercmp(ta, tb):
r = vercmp_part(ra, rb)
return r
+_package_weights_ = {"pre":-2, "p":0, "alpha":-4, "beta":-3, "rc":-1} # dicts are unordered
+_package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list
+
+def relparse(myver):
+ """Parses the last elements of a version number into a triplet, that can
+ later be compared.
+ """
+
+ number = 0
+ p1 = 0
+ p2 = 0
+ mynewver = myver.split('_')
+ if len(mynewver) == 2:
+ # an _package_weights_
+ number = float(mynewver[0])
+ match = 0
+ for x in _package_ends_:
+ elen = len(x)
+ if mynewver[1][:elen] == x:
+ match = 1
+ p1 = _package_weights_[x]
+ try:
+ p2 = float(mynewver[1][elen:])
+ except:
+ p2 = 0
+ break
+ if not match:
+ # normal number or number with letter at end
+ divider = len(myver)-1
+ if myver[divider:] not in "1234567890":
+ # letter at end
+ p1 = ord(myver[divider:])
+ number = float(myver[0:divider])
+ else:
+ number = float(myver)
+ else:
+ # normal number or number with letter at end
+ divider = len(myver)-1
+ if myver[divider:] not in "1234567890":
+ #letter at end
+ p1 = ord(myver[divider:])
+ number = float(myver[0:divider])
+ else:
+ number = float(myver)
+ return [number, p1, p2]
+
+__vercmp_cache__ = {}
+
+def vercmp_string(val1, val2):
+ """This takes two version strings and returns an integer to tell you whether
+ the versions are the same, val1>val2 or val2>val1.
+ """
+
+ # quick short-circuit
+ if val1 == val2:
+ return 0
+ valkey = val1 + " " + val2
+
+ # cache lookup
+ try:
+ return __vercmp_cache__[valkey]
+ try:
+ return - __vercmp_cache__[val2 + " " + val1]
+ except KeyError:
+ pass
+ except KeyError:
+ pass
+
+ # consider 1_p2 vc 1.1
+ # after expansion will become (1_p2,0) vc (1,1)
+ # then 1_p2 is compared with 1 before 0 is compared with 1
+ # to solve the bug we need to convert it to (1,0_p2)
+ # by splitting _prepart part and adding it back _after_expansion
+
+ val1_prepart = val2_prepart = ''
+ if val1.count('_'):
+ val1, val1_prepart = val1.split('_', 1)
+ if val2.count('_'):
+ val2, val2_prepart = val2.split('_', 1)
+
+ # replace '-' by '.'
+ # FIXME: Is it needed? can val1/2 contain '-'?
+
+ val1 = val1.split("-")
+ if len(val1) == 2:
+ val1[0] = val1[0] + "." + val1[1]
+ val2 = val2.split("-")
+ if len(val2) == 2:
+ val2[0] = val2[0] + "." + val2[1]
+
+ val1 = val1[0].split('.')
+ val2 = val2[0].split('.')
+
+ # add back decimal point so that .03 does not become "3" !
+ for x in range(1, len(val1)):
+ if val1[x][0] == '0' :
+ val1[x] = '.' + val1[x]
+ for x in range(1, len(val2)):
+ if val2[x][0] == '0' :
+ val2[x] = '.' + val2[x]
+
+ # extend varion numbers
+ if len(val2) < len(val1):
+ val2.extend(["0"]*(len(val1)-len(val2)))
+ elif len(val1) < len(val2):
+ val1.extend(["0"]*(len(val2)-len(val1)))
+
+ # add back _prepart tails
+ if val1_prepart:
+ val1[-1] += '_' + val1_prepart
+ if val2_prepart:
+ val2[-1] += '_' + val2_prepart
+ # The above code will extend version numbers out so they
+ # have the same number of digits.
+ for x in range(0, len(val1)):
+ cmp1 = relparse(val1[x])
+ cmp2 = relparse(val2[x])
+ for y in range(0, 3):
+ myret = cmp1[y] - cmp2[y]
+ if myret != 0:
+ __vercmp_cache__[valkey] = myret
+ return myret
+ __vercmp_cache__[valkey] = 0
+ return 0
+
def explode_deps(s):
"""
Take an RDEPENDS style string of format:
@@ -154,26 +291,22 @@ def _print_trace(body, line):
"""
Print the Environment of a Text Body
"""
- import bb
-
# print the environment of the method
bb.msg.error(bb.msg.domain.Util, "Printing the environment of the function")
- min_line = max(1,line-4)
- max_line = min(line+4,len(body)-1)
- for i in range(min_line,max_line+1):
+ min_line = max(1, line-4)
+ max_line = min(line + 4, len(body)-1)
+ for i in range(min_line, max_line + 1):
bb.msg.error(bb.msg.domain.Util, "\t%.4d:%s" % (i, body[i-1]) )
-def better_compile(text, file, realfile):
+def better_compile(text, file, realfile, mode = "exec"):
"""
A better compile method. This method
will print the offending lines.
"""
try:
- return compile(text, file, "exec")
- except Exception, e:
- import bb,sys
-
+ return compile(text, file, mode)
+ except Exception as e:
# split the text into lines again
body = text.split('\n')
bb.msg.error(bb.msg.domain.Util, "Error in compiling python function in: ", realfile)
@@ -191,18 +324,18 @@ def better_exec(code, context, text, realfile):
print the lines that are responsible for the
error.
"""
- import bb,sys
+ import bb.parse
try:
- exec code in context
+ exec(code, _context, context)
except:
- (t,value,tb) = sys.exc_info()
+ (t, value, tb) = sys.exc_info()
if t in [bb.parse.SkipPackage, bb.build.FuncFailed]:
raise
# print the Header of the Error Message
bb.msg.error(bb.msg.domain.Util, "Error in executing python function in: %s" % realfile)
- bb.msg.error(bb.msg.domain.Util, "Exception:%s Message:%s" % (t,value) )
+ bb.msg.error(bb.msg.domain.Util, "Exception:%s Message:%s" % (t, value))
# let us find the line number now
while tb.tb_next:
@@ -212,48 +345,14 @@ def better_exec(code, context, text, realfile):
line = traceback.tb_lineno(tb)
_print_trace( text.split('\n'), line )
-
+
raise
-def Enum(*names):
- """
- A simple class to give Enum support
- """
-
- assert names, "Empty enums are not supported"
-
- class EnumClass(object):
- __slots__ = names
- def __iter__(self): return iter(constants)
- def __len__(self): return len(constants)
- def __getitem__(self, i): return constants[i]
- def __repr__(self): return 'Enum' + str(names)
- def __str__(self): return 'enum ' + str(constants)
-
- class EnumValue(object):
- __slots__ = ('__value')
- def __init__(self, value): self.__value = value
- Value = property(lambda self: self.__value)
- EnumType = property(lambda self: EnumType)
- def __hash__(self): return hash(self.__value)
- def __cmp__(self, other):
- # C fans might want to remove the following assertion
- # to make all enums comparable by ordinal value {;))
- assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
- return cmp(self.__value, other.__value)
- def __invert__(self): return constants[maximum - self.__value]
- def __nonzero__(self): return bool(self.__value)
- def __repr__(self): return str(names[self.__value])
-
- maximum = len(names) - 1
- constants = [None] * len(names)
- for i, each in enumerate(names):
- val = EnumValue(i)
- setattr(EnumClass, each, val)
- constants[i] = val
- constants = tuple(constants)
- EnumType = EnumClass()
- return EnumType
+def simple_exec(code, context):
+ exec(code, _context, context)
+
+def better_eval(source, locals):
+ return eval(source, _context, locals)
def lockfile(name):
"""
@@ -262,37 +361,36 @@ def lockfile(name):
"""
path = os.path.dirname(name)
if not os.path.isdir(path):
- import bb, sys
bb.msg.error(bb.msg.domain.Util, "Error, lockfile path does not exist!: %s" % path)
sys.exit(1)
while True:
# If we leave the lockfiles lying around there is no problem
# but we should clean up after ourselves. This gives potential
- # for races though. To work around this, when we acquire the lock
- # we check the file we locked was still the lock file on disk.
- # by comparing inode numbers. If they don't match or the lockfile
+ # for races though. To work around this, when we acquire the lock
+ # we check the file we locked was still the lock file on disk.
+ # by comparing inode numbers. If they don't match or the lockfile
# no longer exists, we start again.
- # This implementation is unfair since the last person to request the
+ # This implementation is unfair since the last person to request the
# lock is the most likely to win it.
try:
- lf = open(name, "a+")
+ lf = open(name, "a + ")
fcntl.flock(lf.fileno(), fcntl.LOCK_EX)
statinfo = os.fstat(lf.fileno())
if os.path.exists(lf.name):
- statinfo2 = os.stat(lf.name)
- if statinfo.st_ino == statinfo2.st_ino:
- return lf
+ statinfo2 = os.stat(lf.name)
+ if statinfo.st_ino == statinfo2.st_ino:
+ return lf
# File no longer exists or changed, retry
lf.close
- except Exception, e:
+ except Exception as e:
continue
def unlockfile(lf):
"""
- Unlock a file locked using lockfile()
+ Unlock a file locked using lockfile()
"""
os.unlink(lf.name)
fcntl.flock(lf.fileno(), fcntl.LOCK_UN)
@@ -308,7 +406,7 @@ def md5_file(filename):
except ImportError:
import md5
m = md5.new()
-
+
for line in open(filename):
m.update(line)
return m.hexdigest()
@@ -368,19 +466,17 @@ def filter_environment(good_vars):
are not known and may influence the build in a negative way.
"""
- import bb
-
removed_vars = []
for key in os.environ.keys():
if key in good_vars:
continue
-
+
removed_vars.append(key)
os.unsetenv(key)
del os.environ[key]
if len(removed_vars):
- bb.debug(1, "Removed the following variables from the environment:", ",".join(removed_vars))
+ bb.msg.debug(1, bb.msg.domain.Util, "Removed the following variables from the environment:", ",".join(removed_vars))
return removed_vars
@@ -410,7 +506,7 @@ def build_environment(d):
"""
Build an environment from all exported variables.
"""
- import bb
+ import bb.data
for var in bb.data.keys(d):
export = bb.data.getVarFlag(var, "export", d)
if export:
@@ -419,7 +515,7 @@ def build_environment(d):
def prunedir(topdir):
# Delete everything reachable from the directory named in 'topdir'.
# CAUTION: This is dangerous!
- for root, dirs, files in os.walk(topdir, topdown=False):
+ for root, dirs, files in os.walk(topdir, topdown = False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
@@ -434,7 +530,7 @@ def prunedir(topdir):
# but thats possibly insane and suffixes is probably going to be small
#
def prune_suffix(var, suffixes, d):
- # See if var ends with any of the suffixes listed and
+ # See if var ends with any of the suffixes listed and
# remove it if found
for suffix in suffixes:
if var.endswith(suffix):
@@ -446,169 +542,167 @@ def mkdirhier(dir):
directory already exists like os.makedirs
"""
- bb.debug(3, "mkdirhier(%s)" % dir)
+ bb.msg.debug(3, bb.msg.domain.Util, "mkdirhier(%s)" % dir)
try:
os.makedirs(dir)
- bb.debug(2, "created " + dir)
- except OSError, e:
- if e.errno != 17: raise e
-
-import stat
+ bb.msg.debug(2, bb.msg.domain.Util, "created " + dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise e
-def movefile(src,dest,newmtime=None,sstat=None):
+def movefile(src, dest, newmtime = None, sstat = None):
"""Moves a file from src to dest, preserving all permissions and
attributes; mtime will be preserved even when moving across
filesystems. Returns true on success and false on failure. Move is
atomic.
"""
- #print "movefile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")"
+ #print "movefile(" + src + "," + dest + "," + str(newmtime) + "," + str(sstat) + ")"
try:
if not sstat:
- sstat=os.lstat(src)
- except Exception, e:
- print "movefile: Stating source file failed...", e
+ sstat = os.lstat(src)
+ except Exception as e:
+ print("movefile: Stating source file failed...", e)
return None
- destexists=1
+ destexists = 1
try:
- dstat=os.lstat(dest)
+ dstat = os.lstat(dest)
except:
- dstat=os.lstat(os.path.dirname(dest))
- destexists=0
+ dstat = os.lstat(os.path.dirname(dest))
+ destexists = 0
if destexists:
if stat.S_ISLNK(dstat[stat.ST_MODE]):
try:
os.unlink(dest)
- destexists=0
- except Exception, e:
+ destexists = 0
+ except Exception as e:
pass
if stat.S_ISLNK(sstat[stat.ST_MODE]):
try:
- target=os.readlink(src)
+ target = os.readlink(src)
if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
os.unlink(dest)
- os.symlink(target,dest)
+ os.symlink(target, dest)
#os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
os.unlink(src)
return os.lstat(dest)
- except Exception, e:
- print "movefile: failed to properly create symlink:", dest, "->", target, e
+ except Exception as e:
+ print("movefile: failed to properly create symlink:", dest, "->", target, e)
return None
- renamefailed=1
- if sstat[stat.ST_DEV]==dstat[stat.ST_DEV]:
+ renamefailed = 1
+ if sstat[stat.ST_DEV] == dstat[stat.ST_DEV]:
try:
- ret=os.rename(src,dest)
- renamefailed=0
- except Exception, e:
- import errno
- if e[0]!=errno.EXDEV:
+ os.rename(src, dest)
+ renamefailed = 0
+ except Exception as e:
+ if e[0] != errno.EXDEV:
# Some random error.
- print "movefile: Failed to move", src, "to", dest, e
+ print("movefile: Failed to move", src, "to", dest, e)
return None
# Invalid cross-device-link 'bind' mounted or actually Cross-Device
if renamefailed:
- didcopy=0
+ didcopy = 0
if stat.S_ISREG(sstat[stat.ST_MODE]):
try: # For safety copy then move it over.
- shutil.copyfile(src,dest+"#new")
- os.rename(dest+"#new",dest)
- didcopy=1
- except Exception, e:
- print 'movefile: copy', src, '->', dest, 'failed.', e
+ shutil.copyfile(src, dest + "#new")
+ os.rename(dest + "#new", dest)
+ didcopy = 1
+ except Exception as e:
+ print('movefile: copy', src, '->', dest, 'failed.', e)
return None
else:
#we don't yet handle special, so we need to fall back to /bin/mv
- a=getstatusoutput("/bin/mv -f "+"'"+src+"' '"+dest+"'")
- if a[0]!=0:
- print "movefile: Failed to move special file:" + src + "' to '" + dest + "'", a
+ a = getstatusoutput("/bin/mv -f " + "'" + src + "' '" + dest + "'")
+ if a[0] != 0:
+ print("movefile: Failed to move special file:" + src + "' to '" + dest + "'", a)
return None # failure
try:
if didcopy:
- os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
+ os.lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID])
os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
os.unlink(src)
- except Exception, e:
- print "movefile: Failed to chown/chmod/unlink", dest, e
+ except Exception as e:
+ print("movefile: Failed to chown/chmod/unlink", dest, e)
return None
if newmtime:
- os.utime(dest,(newmtime,newmtime))
+ os.utime(dest, (newmtime, newmtime))
else:
os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
- newmtime=sstat[stat.ST_MTIME]
+ newmtime = sstat[stat.ST_MTIME]
return newmtime
-def copyfile(src,dest,newmtime=None,sstat=None):
+def copyfile(src, dest, newmtime = None, sstat = None):
"""
Copies a file from src to dest, preserving all permissions and
attributes; mtime will be preserved even when moving across
filesystems. Returns true on success and false on failure.
"""
- #print "copyfile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")"
+ #print "copyfile(" + src + "," + dest + "," + str(newmtime) + "," + str(sstat) + ")"
try:
if not sstat:
- sstat=os.lstat(src)
- except Exception, e:
- print "copyfile: Stating source file failed...", e
+ sstat = os.lstat(src)
+ except Exception as e:
+ print("copyfile: Stating source file failed...", e)
return False
- destexists=1
+ destexists = 1
try:
- dstat=os.lstat(dest)
+ dstat = os.lstat(dest)
except:
- dstat=os.lstat(os.path.dirname(dest))
- destexists=0
+ dstat = os.lstat(os.path.dirname(dest))
+ destexists = 0
if destexists:
if stat.S_ISLNK(dstat[stat.ST_MODE]):
try:
os.unlink(dest)
- destexists=0
- except Exception, e:
+ destexists = 0
+ except Exception as e:
pass
if stat.S_ISLNK(sstat[stat.ST_MODE]):
try:
- target=os.readlink(src)
+ target = os.readlink(src)
if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
os.unlink(dest)
- os.symlink(target,dest)
+ os.symlink(target, dest)
#os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
return os.lstat(dest)
- except Exception, e:
- print "copyfile: failed to properly create symlink:", dest, "->", target, e
+ except Exception as e:
+ print("copyfile: failed to properly create symlink:", dest, "->", target, e)
return False
if stat.S_ISREG(sstat[stat.ST_MODE]):
- try: # For safety copy then move it over.
- shutil.copyfile(src,dest+"#new")
- os.rename(dest+"#new",dest)
- except Exception, e:
- print 'copyfile: copy', src, '->', dest, 'failed.', e
- return False
+ try: # For safety copy then move it over.
+ shutil.copyfile(src, dest + "#new")
+ os.rename(dest + "#new", dest)
+ except Exception as e:
+ print('copyfile: copy', src, '->', dest, 'failed.', e)
+ return False
else:
- #we don't yet handle special, so we need to fall back to /bin/mv
- a=getstatusoutput("/bin/cp -f "+"'"+src+"' '"+dest+"'")
- if a[0]!=0:
- print "copyfile: Failed to copy special file:" + src + "' to '" + dest + "'", a
- return False # failure
+ #we don't yet handle special, so we need to fall back to /bin/mv
+ a = getstatusoutput("/bin/cp -f " + "'" + src + "' '" + dest + "'")
+ if a[0] != 0:
+ print("copyfile: Failed to copy special file:" + src + "' to '" + dest + "'", a)
+ return False # failure
try:
- os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
+ os.lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID])
os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
- except Exception, e:
- print "copyfile: Failed to chown/chmod/unlink", dest, e
+ except Exception as e:
+ print("copyfile: Failed to chown/chmod/unlink", dest, e)
return False
if newmtime:
- os.utime(dest,(newmtime,newmtime))
+ os.utime(dest, (newmtime, newmtime))
else:
os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
- newmtime=sstat[stat.ST_MTIME]
+ newmtime = sstat[stat.ST_MTIME]
return newmtime
def which(path, item, direction = 0):