diff options
| author | Chris Larson <clarson@kergoth.com> | 2009-07-19 10:05:52 -0700 | 
|---|---|---|
| committer | Richard Purdie <rpurdie@linux.intel.com> | 2010-03-22 14:54:42 +0000 | 
| commit | f8c6db95d7734e3f4fc7eaf82eda6d59721b3fbb (patch) | |
| tree | 18e1882916586b69460252a3465cce9ccdbbbb04 | |
| parent | b7d175a18a0fab65ff4022e15c68e079296c8e82 (diff) | |
| download | openembedded-core-f8c6db95d7734e3f4fc7eaf82eda6d59721b3fbb.tar.gz openembedded-core-f8c6db95d7734e3f4fc7eaf82eda6d59721b3fbb.tar.bz2 openembedded-core-f8c6db95d7734e3f4fc7eaf82eda6d59721b3fbb.zip | |
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__.py | 914 | ||||
| -rw-r--r-- | bitbake/lib/bb/utils.py | 759 | 
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__ = [      "providers",   ] -whitespace = '\t\n\x0b\x0c\r ' -lowercase = 'abcdefghijklmnopqrstuvwxyz' -  import sys, os, types, re, string, bb -from bb import msg  #projectdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))  projectdir = os.getcwd() @@ -109,914 +105,14 @@ def fatal(*args):      bb.msg.fatal(None, ''.join(args)) -####################################################################### -####################################################################### -# -# SECTION: File -# -# PURPOSE: Basic file and directory tree related functions -# -####################################################################### -####################################################################### - -def mkdirhier(dir): -    """Create a directory like 'mkdir -p', but does not complain if -    directory already exists like os.makedirs -    """ - -    debug(3, "mkdirhier(%s)" % dir) -    try: -        os.makedirs(dir) -        debug(2, "created " + dir) -    except OSError, e: -        if e.errno != 17: raise e - - -####################################################################### - -import stat - -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)+")" -    try: -        if not sstat: -            sstat=os.lstat(src) -    except Exception, e: -        print "movefile: Stating source file failed...", e -        return None - -    destexists=1 -    try: -        dstat=os.lstat(dest) -    except: -        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: -                pass - -    if stat.S_ISLNK(sstat[stat.ST_MODE]): -        try: -            target=os.readlink(src) -            if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): -                os.unlink(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 -            return None - -    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: -                # Some random error. -                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 -        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 -                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 -                return None # failure -        try: -            if didcopy: -                missingos.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 -            return None - -    if newmtime: -        os.utime(dest,(newmtime,newmtime)) -    else: -        os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) -        newmtime=sstat[stat.ST_MTIME] -    return newmtime - -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. -    """ -    import os, stat, shutil - -    #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 -        return False - -    destexists=1 -    try: -        dstat=os.lstat(dest) -    except: -        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: -                pass - -    if stat.S_ISLNK(sstat[stat.ST_MODE]): -        try: -            target=os.readlink(src) -            if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): -                os.unlink(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 -            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 -    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 -    try: -        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 -        return False - -    if newmtime: -        os.utime(dest,(newmtime,newmtime)) -    else: -        os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) -        newmtime=sstat[stat.ST_MTIME] -    return newmtime - -####################################################################### - -def which(path, item, direction = 0): -    """ -    Locate a file in a PATH -    """ - -    paths = (path or "").split(':') -    if direction != 0: -        paths.reverse() - -    for p in (path or "").split(':'): -        next = os.path.join(p, item) -        if os.path.exists(next): -            return next - -    return "" - -####################################################################### - - - - -####################################################################### -####################################################################### -# -# SECTION: Dependency -# -# PURPOSE: Compare build & run dependencies -# -####################################################################### -####################################################################### - -def tokenize(mystring): -    """Breaks a string like 'foo? (bar) oni? (blah (blah))' into (possibly embedded) lists: - -    >>> tokenize("x") -    ['x'] -    >>> tokenize("x y") -    ['x', 'y'] -    >>> tokenize("(x y)") -    [['x', 'y']] -    >>> tokenize("(x y) b c") -    [['x', 'y'], 'b', 'c'] -    >>> tokenize("foo? (bar) oni? (blah (blah))") -    ['foo?', ['bar'], 'oni?', ['blah', ['blah']]] -    >>> tokenize("sys-apps/linux-headers nls? (sys-devel/gettext)") -    ['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']] -    """ - -    newtokens = [] -    curlist   = newtokens -    prevlists = [] -    level     = 0 -    accum     = "" -    for x in mystring: -        if x=="(": -            if accum: -                curlist.append(accum) -                accum="" -            prevlists.append(curlist) -            curlist=[] -            level=level+1 -        elif x==")": -            if accum: -                curlist.append(accum) -                accum="" -            if level==0: -                print "!!! tokenizer: Unmatched left parenthesis in:\n'"+mystring+"'" -                return None -            newlist=curlist -            curlist=prevlists.pop() -            curlist.append(newlist) -            level=level-1 -        elif x in whitespace: -            if accum: -                curlist.append(accum) -                accum="" -        else: -            accum=accum+x -    if accum: -        curlist.append(accum) -    if (level!=0): -        print "!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+mystring+"'" -        return None -    return newtokens - - -####################################################################### - -def evaluate(tokens,mydefines,allon=0): -    """Removes tokens based on whether conditional definitions exist or not. -    Recognizes ! - -    >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}) -    ['sys-apps/linux-headers'] - -    Negate the flag: - -    >>> evaluate(['sys-apps/linux-headers', '!nls?', ['sys-devel/gettext']], {}) -    ['sys-apps/linux-headers', ['sys-devel/gettext']] - -    Define 'nls': - -    >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {"nls":1}) -    ['sys-apps/linux-headers', ['sys-devel/gettext']] - -    Turn allon on: - -    >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}, True) -    ['sys-apps/linux-headers', ['sys-devel/gettext']] -    """ - -    if tokens == None: -        return None -    mytokens = tokens + []        # this copies the list -    pos = 0 -    while pos < len(mytokens): -        if type(mytokens[pos]) == types.ListType: -            evaluate(mytokens[pos], mydefines) -            if not len(mytokens[pos]): -                del mytokens[pos] -                continue -        elif mytokens[pos][-1] == "?": -            cur = mytokens[pos][:-1] -            del mytokens[pos] -            if allon: -                if cur[0] == "!": -                    del mytokens[pos] -            else: -                if cur[0] == "!": -                    if (cur[1:] in mydefines) and (pos < len(mytokens)): -                        del mytokens[pos] -                        continue -                elif (cur not in mydefines) and (pos < len(mytokens)): -                    del mytokens[pos] -                    continue -        pos = pos + 1 -    return mytokens - - -####################################################################### - -def flatten(mytokens): -    """Converts nested arrays into a flat arrays: - -    >>> flatten([1,[2,3]]) -    [1, 2, 3] -    >>> flatten(['sys-apps/linux-headers', ['sys-devel/gettext']]) -    ['sys-apps/linux-headers', 'sys-devel/gettext'] -    """ - -    newlist=[] -    for x in mytokens: -        if type(x)==types.ListType: -            newlist.extend(flatten(x)) -        else: -            newlist.append(x) -    return newlist - - -####################################################################### - -_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: - -    >>> relparse('1.2_pre3') -    [1.2, -2, 3.0] -    >>> relparse('1.2b') -    [1.2, 98, 0] -    >>> relparse('1.2') -    [1.2, 0, 0] -    """ - -    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] - - -####################################################################### - -__ververify_cache__ = {} - -def ververify(myorigval,silent=1): -    """Returns 1 if given a valid version string, els 0. Valid versions are in the format - -    <v1>.<v2>...<vx>[a-z,_{_package_weights_}[vy]] - -    >>> ververify('2.4.20') -    1 -    >>> ververify('2.4..20')        # two dots -    0 -    >>> ververify('2.x.20')            # 'x' is not numeric -    0 -    >>> ververify('2.4.20a') -    1 -    >>> ververify('2.4.20cvs')        # only one trailing letter -    0 -    >>> ververify('1a') -    1 -    >>> ververify('test_a')            # no version at all -    0 -    >>> ververify('2.4.20_beta1') -    1 -    >>> ververify('2.4.20_beta') -    1 -    >>> ververify('2.4.20_wrongext')    # _wrongext is no valid trailer -    0 -    """ - -    # Lookup the cache first -    try: -        return __ververify_cache__[myorigval] -    except KeyError: -        pass - -    if len(myorigval) == 0: -        if not silent: -            error("package version is empty") -        __ververify_cache__[myorigval] = 0 -        return 0 -    myval = myorigval.split('.') -    if len(myval)==0: -        if not silent: -            error("package name has empty version string") -        __ververify_cache__[myorigval] = 0 -        return 0 -    # all but the last version must be a numeric -    for x in myval[:-1]: -        if not len(x): -            if not silent: -                error("package version has two points in a row") -            __ververify_cache__[myorigval] = 0 -            return 0 -        try: -            foo = int(x) -        except: -            if not silent: -                error("package version contains non-numeric '"+x+"'") -            __ververify_cache__[myorigval] = 0 -            return 0 -    if not len(myval[-1]): -            if not silent: -                error("package version has trailing dot") -            __ververify_cache__[myorigval] = 0 -            return 0 -    try: -        foo = int(myval[-1]) -        __ververify_cache__[myorigval] = 1 -        return 1 -    except: -        pass - -    # ok, our last component is not a plain number or blank, let's continue -    if myval[-1][-1] in lowercase: -        try: -            foo = int(myval[-1][:-1]) -            return 1 -            __ververify_cache__[myorigval] = 1 -            # 1a, 2.0b, etc. -        except: -            pass -    # ok, maybe we have a 1_alpha or 1_beta2; let's see -    ep=string.split(myval[-1],"_") -    if len(ep)!= 2: -        if not silent: -            error("package version has more than one letter at then end") -        __ververify_cache__[myorigval] = 0 -        return 0 -    try: -        foo = string.atoi(ep[0]) -    except: -        # this needs to be numeric, i.e. the "1" in "1_alpha" -        if not silent: -            error("package version must have numeric part before the '_'") -        __ververify_cache__[myorigval] = 0 -        return 0 - -    for mye in _package_ends_: -        if ep[1][0:len(mye)] == mye: -            if len(mye) == len(ep[1]): -                # no trailing numeric is ok -                __ververify_cache__[myorigval] = 1 -                return 1 -            else: -                try: -                    foo = string.atoi(ep[1][len(mye):]) -                    __ververify_cache__[myorigval] = 1 -                    return 1 -                except: -                    # if no _package_weights_ work, *then* we return 0 -                    pass -    if not silent: -        error("package version extension after '_' is invalid") -    __ververify_cache__[myorigval] = 0 -    return 0 - - -def isjustname(mypkg): -    myparts = string.split(mypkg,'-') -    for x in myparts: -        if ververify(x): -            return 0 -    return 1 - - -_isspecific_cache_={} - -def isspecific(mypkg): -    "now supports packages with no category" -    try: -        return __isspecific_cache__[mypkg] -    except: -        pass - -    mysplit = string.split(mypkg,"/") -    if not isjustname(mysplit[-1]): -            __isspecific_cache__[mypkg] = 1 -            return 1 -    __isspecific_cache__[mypkg] = 0 -    return 0 - - -####################################################################### - -__pkgsplit_cache__={} - -def pkgsplit(mypkg, silent=1): - -    """This function can be used as a package verification function. If -    it is a valid name, pkgsplit will return a list containing: -    [pkgname, pkgversion(norev), pkgrev ]. - -    >>> pkgsplit('') -    >>> pkgsplit('x') -    >>> pkgsplit('x-') -    >>> pkgsplit('-1') -    >>> pkgsplit('glibc-1.2-8.9-r7') -    >>> pkgsplit('glibc-2.2.5-r7') -    ['glibc', '2.2.5', 'r7'] -    >>> pkgsplit('foo-1.2-1') -    >>> pkgsplit('Mesa-3.0') -    ['Mesa', '3.0', 'r0'] -    """ - -    try: -        return __pkgsplit_cache__[mypkg] -    except KeyError: -        pass - -    myparts = string.split(mypkg,'-') -    if len(myparts) < 2: -        if not silent: -            error("package name without name or version part") -        __pkgsplit_cache__[mypkg] = None -        return None -    for x in myparts: -        if len(x) == 0: -            if not silent: -                error("package name with empty name or version part") -            __pkgsplit_cache__[mypkg] = None -            return None -    # verify rev -    revok = 0 -    myrev = myparts[-1] -    ververify(myrev, silent) -    if len(myrev) and myrev[0] == "r": -        try: -            string.atoi(myrev[1:]) -            revok = 1 -        except: -            pass -    if revok: -        if ververify(myparts[-2]): -            if len(myparts) == 2: -                __pkgsplit_cache__[mypkg] = None -                return None -            else: -                for x in myparts[:-2]: -                    if ververify(x): -                        __pkgsplit_cache__[mypkg]=None -                        return None -                        # names can't have versiony looking parts -                myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]] -                __pkgsplit_cache__[mypkg]=myval -                return myval -        else: -            __pkgsplit_cache__[mypkg] = None -            return None - -    elif ververify(myparts[-1],silent): -        if len(myparts)==1: -            if not silent: -                print "!!! Name error in",mypkg+": missing name part." -            __pkgsplit_cache__[mypkg]=None -            return None -        else: -            for x in myparts[:-1]: -                if ververify(x): -                    if not silent: error("package name has multiple version parts") -                    __pkgsplit_cache__[mypkg] = None -                    return None -            myval = [string.join(myparts[:-1],"-"), myparts[-1],"r0"] -            __pkgsplit_cache__[mypkg] = myval -            return myval -    else: -        __pkgsplit_cache__[mypkg] = None -        return None - - -####################################################################### - -__catpkgsplit_cache__ = {} - -def catpkgsplit(mydata,silent=1): -    """returns [cat, pkgname, version, rev ] - -    >>> catpkgsplit('sys-libs/glibc-1.2-r7') -    ['sys-libs', 'glibc', '1.2', 'r7'] -    >>> catpkgsplit('glibc-1.2-r7') -    [None, 'glibc', '1.2', 'r7'] -    """ - -    try: -        return __catpkgsplit_cache__[mydata] -    except KeyError: -        pass - -    cat = os.path.basename(os.path.dirname(mydata)) -    mydata = os.path.join(cat, os.path.basename(mydata)) -    if mydata[-3:] == '.bb': -        mydata = mydata[:-3] - -    mysplit = mydata.split("/") -    p_split = None -    splitlen = len(mysplit) -    if splitlen == 1: -        retval = [None] -        p_split = pkgsplit(mydata,silent) -    else: -        retval = [mysplit[splitlen - 2]] -        p_split = pkgsplit(mysplit[splitlen - 1],silent) -    if not p_split: -        __catpkgsplit_cache__[mydata] = None -        return None -    retval.extend(p_split) -    __catpkgsplit_cache__[mydata] = retval -    return retval - - -####################################################################### - -__vercmp_cache__ = {} - -def vercmp(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. - -    >>> vercmp('1', '2') -    -1.0 -    >>> vercmp('2', '1') -    1.0 -    >>> vercmp('1', '1.0') -    0 -    >>> vercmp('1', '1.1') -    -1.0 -    >>> vercmp('1.1', '1_p2') -    1.0 -    """ - -    # 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 = string.split(val1,'-') -    if len(val1) == 2: -        val1[0] = val1[0] +"."+ val1[1] -    val2 = string.split(val2,'-') -    if len(val2) == 2: -        val2[0] = val2[0] +"."+ val2[1] - -    val1 = string.split(val1[0],'.') -    val2 = string.split(val2[0],'.') - -    # 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 pkgcmp(pkg1,pkg2): -    """ Compares two packages, which should have been split via -    pkgsplit(). if the return value val is less than zero, then pkg2 is -    newer than pkg1, zero if equal and positive if older. - -    >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r7']) -    0 -    >>> pkgcmp(['glibc', '2.2.5', 'r4'], ['glibc', '2.2.5', 'r7']) -    -1 -    >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r2']) -    1 -    """ - -    mycmp = vercmp(pkg1[1],pkg2[1]) -    if mycmp > 0: -        return 1 -    if mycmp < 0: -        return -1 -    r1=string.atoi(pkg1[2][1:]) -    r2=string.atoi(pkg2[2][1:]) -    if r1 > r2: -        return 1 -    if r2 > r1: -        return -1 -    return 0 - - -####################################################################### - -def dep_parenreduce(mysplit, mypos=0): -    """Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists: - -    >>> dep_parenreduce(['']) -    [''] -    >>> dep_parenreduce(['1', '2', '3']) -    ['1', '2', '3'] -    >>> dep_parenreduce(['1', '(', '2', '3', ')', '4']) -    ['1', ['2', '3'], '4'] -    """ - -    while mypos < len(mysplit): -        if mysplit[mypos] == "(": -            firstpos = mypos -            mypos = mypos + 1 -            while mypos < len(mysplit): -                if mysplit[mypos] == ")": -                    mysplit[firstpos:mypos+1] = [mysplit[firstpos+1:mypos]] -                    mypos = firstpos -                    break -                elif mysplit[mypos] == "(": -                    # recurse -                    mysplit = dep_parenreduce(mysplit,mypos) -                mypos = mypos + 1 -        mypos = mypos + 1 -    return mysplit - - -def dep_opconvert(mysplit, myuse): -    "Does dependency operator conversion" - -    mypos   = 0 -    newsplit = [] -    while mypos < len(mysplit): -        if type(mysplit[mypos]) == types.ListType: -            newsplit.append(dep_opconvert(mysplit[mypos],myuse)) -            mypos += 1 -        elif mysplit[mypos] == ")": -            # mismatched paren, error -            return None -        elif mysplit[mypos]=="||": -            if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType): -                # || must be followed by paren'd list -                return None -            try: -                mynew = dep_opconvert(mysplit[mypos+1],myuse) -            except Exception, e: -                error("unable to satisfy OR dependancy: " + string.join(mysplit," || ")) -                raise e -            mynew[0:0] = ["||"] -            newsplit.append(mynew) -            mypos += 2 -        elif mysplit[mypos][-1] == "?": -            # use clause, i.e "gnome? ( foo bar )" -            # this is a quick and dirty hack so that repoman can enable all USE vars: -            if (len(myuse) == 1) and (myuse[0] == "*"): -                # enable it even if it's ! (for repoman) but kill it if it's -                # an arch variable that isn't for this arch. XXX Sparc64? -                if (mysplit[mypos][:-1] not in settings.usemask) or \ -                        (mysplit[mypos][:-1]==settings["ARCH"]): -                    enabled=1 -                else: -                    enabled=0 -            else: -                if mysplit[mypos][0] == "!": -                    myusevar = mysplit[mypos][1:-1] -                    enabled = not myusevar in myuse -                    #if myusevar in myuse: -                    #    enabled = 0 -                    #else: -                    #    enabled = 1 -                else: -                    myusevar=mysplit[mypos][:-1] -                    enabled = myusevar in myuse -                    #if myusevar in myuse: -                    #    enabled=1 -                    #else: -                    #    enabled=0 -            if (mypos +2 < len(mysplit)) and (mysplit[mypos+2] == ":"): -                # colon mode -                if enabled: -                    # choose the first option -                    if type(mysplit[mypos+1]) == types.ListType: -                        newsplit.append(dep_opconvert(mysplit[mypos+1],myuse)) -                    else: -                        newsplit.append(mysplit[mypos+1]) -                else: -                    # choose the alternate option -                    if type(mysplit[mypos+1]) == types.ListType: -                        newsplit.append(dep_opconvert(mysplit[mypos+3],myuse)) -                    else: -                        newsplit.append(mysplit[mypos+3]) -                mypos += 4 -            else: -                # normal use mode -                if enabled: -                    if type(mysplit[mypos+1]) == types.ListType: -                        newsplit.append(dep_opconvert(mysplit[mypos+1],myuse)) -                    else: -                        newsplit.append(mysplit[mypos+1]) -                # otherwise, continue -                mypos += 2 -        else: -            # normal item -            newsplit.append(mysplit[mypos]) -            mypos += 1 -    return newsplit -  # For compatibility  from bb.fetch import MalformedUrl, encodeurl, decodeurl  from bb.data import VarExpandError +from bb.utils import mkdirhier, movefile, copyfile, which +from bb.utils import tokenize, evaluate, flatten +from bb.utils import vercmp, pkgcmp, relparse, ververify +from bb.utils import pkgsplit, catpkgsplit, isjustname, isspecific +from bb.utils import dep_parenreduce, dep_opconvert  if __name__ == "__main__":      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):          if var.endswith(suffix):              return var.replace(suffix, "")      return var + +def mkdirhier(dir): +    """Create a directory like 'mkdir -p', but does not complain if +    directory already exists like os.makedirs +    """ + +    debug(3, "mkdirhier(%s)" % dir) +    try: +        os.makedirs(dir) +        debug(2, "created " + dir) +    except OSError, e: +        if e.errno != 17: raise e + +import stat + +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)+")" +    try: +        if not sstat: +            sstat=os.lstat(src) +    except Exception, e: +        print "movefile: Stating source file failed...", e +        return None + +    destexists=1 +    try: +        dstat=os.lstat(dest) +    except: +        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: +                pass + +    if stat.S_ISLNK(sstat[stat.ST_MODE]): +        try: +            target=os.readlink(src) +            if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): +                os.unlink(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 +            return None + +    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: +                # Some random error. +                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 +        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 +                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 +                return None # failure +        try: +            if didcopy: +                missingos.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 +            return None + +    if newmtime: +        os.utime(dest,(newmtime,newmtime)) +    else: +        os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) +        newmtime=sstat[stat.ST_MTIME] +    return newmtime + +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. +    """ +    import os, stat, shutil + +    #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 +        return False + +    destexists=1 +    try: +        dstat=os.lstat(dest) +    except: +        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: +                pass + +    if stat.S_ISLNK(sstat[stat.ST_MODE]): +        try: +            target=os.readlink(src) +            if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): +                os.unlink(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 +            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 +    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 +    try: +        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 +        return False + +    if newmtime: +        os.utime(dest,(newmtime,newmtime)) +    else: +        os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) +        newmtime=sstat[stat.ST_MTIME] +    return newmtime + +def which(path, item, direction = 0): +    """ +    Locate a file in a PATH +    """ + +    paths = (path or "").split(':') +    if direction != 0: +        paths.reverse() + +    for p in (path or "").split(':'): +        next = os.path.join(p, item) +        if os.path.exists(next): +            return next + +    return "" + +whitespace = '\t\n\x0b\x0c\r ' +lowercase = 'abcdefghijklmnopqrstuvwxyz' + +def tokenize(mystring): +    """Breaks a string like 'foo? (bar) oni? (blah (blah))' into (possibly embedded) lists: + +    >>> tokenize("x") +    ['x'] +    >>> tokenize("x y") +    ['x', 'y'] +    >>> tokenize("(x y)") +    [['x', 'y']] +    >>> tokenize("(x y) b c") +    [['x', 'y'], 'b', 'c'] +    >>> tokenize("foo? (bar) oni? (blah (blah))") +    ['foo?', ['bar'], 'oni?', ['blah', ['blah']]] +    >>> tokenize("sys-apps/linux-headers nls? (sys-devel/gettext)") +    ['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']] +    """ + +    newtokens = [] +    curlist   = newtokens +    prevlists = [] +    level     = 0 +    accum     = "" +    for x in mystring: +        if x=="(": +            if accum: +                curlist.append(accum) +                accum="" +            prevlists.append(curlist) +            curlist=[] +            level=level+1 +        elif x==")": +            if accum: +                curlist.append(accum) +                accum="" +            if level==0: +                print "!!! tokenizer: Unmatched left parenthesis in:\n'"+mystring+"'" +                return None +            newlist=curlist +            curlist=prevlists.pop() +            curlist.append(newlist) +            level=level-1 +        elif x in whitespace: +            if accum: +                curlist.append(accum) +                accum="" +        else: +            accum=accum+x +    if accum: +        curlist.append(accum) +    if (level!=0): +        print "!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+mystring+"'" +        return None +    return newtokens + +def evaluate(tokens,mydefines,allon=0): +    """Removes tokens based on whether conditional definitions exist or not. +    Recognizes ! + +    >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}) +    ['sys-apps/linux-headers'] + +    Negate the flag: + +    >>> evaluate(['sys-apps/linux-headers', '!nls?', ['sys-devel/gettext']], {}) +    ['sys-apps/linux-headers', ['sys-devel/gettext']] + +    Define 'nls': + +    >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {"nls":1}) +    ['sys-apps/linux-headers', ['sys-devel/gettext']] + +    Turn allon on: + +    >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}, True) +    ['sys-apps/linux-headers', ['sys-devel/gettext']] +    """ + +    if tokens == None: +        return None +    mytokens = tokens + []        # this copies the list +    pos = 0 +    while pos < len(mytokens): +        if type(mytokens[pos]) == types.ListType: +            evaluate(mytokens[pos], mydefines) +            if not len(mytokens[pos]): +                del mytokens[pos] +                continue +        elif mytokens[pos][-1] == "?": +            cur = mytokens[pos][:-1] +            del mytokens[pos] +            if allon: +                if cur[0] == "!": +                    del mytokens[pos] +            else: +                if cur[0] == "!": +                    if (cur[1:] in mydefines) and (pos < len(mytokens)): +                        del mytokens[pos] +                        continue +                elif (cur not in mydefines) and (pos < len(mytokens)): +                    del mytokens[pos] +                    continue +        pos = pos + 1 +    return mytokens + +def flatten(mytokens): +    """Converts nested arrays into a flat arrays: + +    >>> flatten([1,[2,3]]) +    [1, 2, 3] +    >>> flatten(['sys-apps/linux-headers', ['sys-devel/gettext']]) +    ['sys-apps/linux-headers', 'sys-devel/gettext'] +    """ + +    newlist=[] +    for x in mytokens: +        if type(x)==types.ListType: +            newlist.extend(flatten(x)) +        else: +            newlist.append(x) +    return newlist + +_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: + +    >>> relparse('1.2_pre3') +    [1.2, -2, 3.0] +    >>> relparse('1.2b') +    [1.2, 98, 0] +    >>> relparse('1.2') +    [1.2, 0, 0] +    """ + +    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] + +__ververify_cache__ = {} + +def ververify(myorigval,silent=1): +    """Returns 1 if given a valid version string, els 0. Valid versions are in the format + +    <v1>.<v2>...<vx>[a-z,_{_package_weights_}[vy]] + +    >>> ververify('2.4.20') +    1 +    >>> ververify('2.4..20')        # two dots +    0 +    >>> ververify('2.x.20')            # 'x' is not numeric +    0 +    >>> ververify('2.4.20a') +    1 +    >>> ververify('2.4.20cvs')        # only one trailing letter +    0 +    >>> ververify('1a') +    1 +    >>> ververify('test_a')            # no version at all +    0 +    >>> ververify('2.4.20_beta1') +    1 +    >>> ververify('2.4.20_beta') +    1 +    >>> ververify('2.4.20_wrongext')    # _wrongext is no valid trailer +    0 +    """ + +    # Lookup the cache first +    try: +        return __ververify_cache__[myorigval] +    except KeyError: +        pass + +    if len(myorigval) == 0: +        if not silent: +            error("package version is empty") +        __ververify_cache__[myorigval] = 0 +        return 0 +    myval = myorigval.split('.') +    if len(myval)==0: +        if not silent: +            error("package name has empty version string") +        __ververify_cache__[myorigval] = 0 +        return 0 +    # all but the last version must be a numeric +    for x in myval[:-1]: +        if not len(x): +            if not silent: +                error("package version has two points in a row") +            __ververify_cache__[myorigval] = 0 +            return 0 +        try: +            foo = int(x) +        except: +            if not silent: +                error("package version contains non-numeric '"+x+"'") +            __ververify_cache__[myorigval] = 0 +            return 0 +    if not len(myval[-1]): +            if not silent: +                error("package version has trailing dot") +            __ververify_cache__[myorigval] = 0 +            return 0 +    try: +        foo = int(myval[-1]) +        __ververify_cache__[myorigval] = 1 +        return 1 +    except: +        pass + +    # ok, our last component is not a plain number or blank, let's continue +    if myval[-1][-1] in lowercase: +        try: +            foo = int(myval[-1][:-1]) +            return 1 +            __ververify_cache__[myorigval] = 1 +            # 1a, 2.0b, etc. +        except: +            pass +    # ok, maybe we have a 1_alpha or 1_beta2; let's see +    ep=string.split(myval[-1],"_") +    if len(ep)!= 2: +        if not silent: +            error("package version has more than one letter at then end") +        __ververify_cache__[myorigval] = 0 +        return 0 +    try: +        foo = string.atoi(ep[0]) +    except: +        # this needs to be numeric, i.e. the "1" in "1_alpha" +        if not silent: +            error("package version must have numeric part before the '_'") +        __ververify_cache__[myorigval] = 0 +        return 0 + +    for mye in _package_ends_: +        if ep[1][0:len(mye)] == mye: +            if len(mye) == len(ep[1]): +                # no trailing numeric is ok +                __ververify_cache__[myorigval] = 1 +                return 1 +            else: +                try: +                    foo = string.atoi(ep[1][len(mye):]) +                    __ververify_cache__[myorigval] = 1 +                    return 1 +                except: +                    # if no _package_weights_ work, *then* we return 0 +                    pass +    if not silent: +        error("package version extension after '_' is invalid") +    __ververify_cache__[myorigval] = 0 +    return 0 + +def isjustname(mypkg): +    myparts = string.split(mypkg,'-') +    for x in myparts: +        if ververify(x): +            return 0 +    return 1 + +_isspecific_cache_={} + +def isspecific(mypkg): +    "now supports packages with no category" +    try: +        return __isspecific_cache__[mypkg] +    except: +        pass + +    mysplit = string.split(mypkg,"/") +    if not isjustname(mysplit[-1]): +            __isspecific_cache__[mypkg] = 1 +            return 1 +    __isspecific_cache__[mypkg] = 0 +    return 0 + +__pkgsplit_cache__={} + +def pkgsplit(mypkg, silent=1): + +    """This function can be used as a package verification function. If +    it is a valid name, pkgsplit will return a list containing: +    [pkgname, pkgversion(norev), pkgrev ]. + +    >>> pkgsplit('') +    >>> pkgsplit('x') +    >>> pkgsplit('x-') +    >>> pkgsplit('-1') +    >>> pkgsplit('glibc-1.2-8.9-r7') +    >>> pkgsplit('glibc-2.2.5-r7') +    ['glibc', '2.2.5', 'r7'] +    >>> pkgsplit('foo-1.2-1') +    >>> pkgsplit('Mesa-3.0') +    ['Mesa', '3.0', 'r0'] +    """ + +    try: +        return __pkgsplit_cache__[mypkg] +    except KeyError: +        pass + +    myparts = string.split(mypkg,'-') +    if len(myparts) < 2: +        if not silent: +            error("package name without name or version part") +        __pkgsplit_cache__[mypkg] = None +        return None +    for x in myparts: +        if len(x) == 0: +            if not silent: +                error("package name with empty name or version part") +            __pkgsplit_cache__[mypkg] = None +            return None +    # verify rev +    revok = 0 +    myrev = myparts[-1] +    ververify(myrev, silent) +    if len(myrev) and myrev[0] == "r": +        try: +            string.atoi(myrev[1:]) +            revok = 1 +        except: +            pass +    if revok: +        if ververify(myparts[-2]): +            if len(myparts) == 2: +                __pkgsplit_cache__[mypkg] = None +                return None +            else: +                for x in myparts[:-2]: +                    if ververify(x): +                        __pkgsplit_cache__[mypkg]=None +                        return None +                        # names can't have versiony looking parts +                myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]] +                __pkgsplit_cache__[mypkg]=myval +                return myval +        else: +            __pkgsplit_cache__[mypkg] = None +            return None + +    elif ververify(myparts[-1],silent): +        if len(myparts)==1: +            if not silent: +                print "!!! Name error in",mypkg+": missing name part." +            __pkgsplit_cache__[mypkg]=None +            return None +        else: +            for x in myparts[:-1]: +                if ververify(x): +                    if not silent: error("package name has multiple version parts") +                    __pkgsplit_cache__[mypkg] = None +                    return None +            myval = [string.join(myparts[:-1],"-"), myparts[-1],"r0"] +            __pkgsplit_cache__[mypkg] = myval +            return myval +    else: +        __pkgsplit_cache__[mypkg] = None +        return None + +__catpkgsplit_cache__ = {} + +def catpkgsplit(mydata,silent=1): +    """returns [cat, pkgname, version, rev ] + +    >>> catpkgsplit('sys-libs/glibc-1.2-r7') +    ['sys-libs', 'glibc', '1.2', 'r7'] +    >>> catpkgsplit('glibc-1.2-r7') +    [None, 'glibc', '1.2', 'r7'] +    """ + +    try: +        return __catpkgsplit_cache__[mydata] +    except KeyError: +        pass + +    cat = os.path.basename(os.path.dirname(mydata)) +    mydata = os.path.join(cat, os.path.basename(mydata)) +    if mydata[-3:] == '.bb': +        mydata = mydata[:-3] + +    mysplit = mydata.split("/") +    p_split = None +    splitlen = len(mysplit) +    if splitlen == 1: +        retval = [None] +        p_split = pkgsplit(mydata,silent) +    else: +        retval = [mysplit[splitlen - 2]] +        p_split = pkgsplit(mysplit[splitlen - 1],silent) +    if not p_split: +        __catpkgsplit_cache__[mydata] = None +        return None +    retval.extend(p_split) +    __catpkgsplit_cache__[mydata] = retval +    return retval + +def pkgcmp(pkg1,pkg2): +    """ Compares two packages, which should have been split via +    pkgsplit(). if the return value val is less than zero, then pkg2 is +    newer than pkg1, zero if equal and positive if older. + +    >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r7']) +    0 +    >>> pkgcmp(['glibc', '2.2.5', 'r4'], ['glibc', '2.2.5', 'r7']) +    -1 +    >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r2']) +    1 +    """ + +    mycmp = vercmp(pkg1[1],pkg2[1]) +    if mycmp > 0: +        return 1 +    if mycmp < 0: +        return -1 +    r1=string.atoi(pkg1[2][1:]) +    r2=string.atoi(pkg2[2][1:]) +    if r1 > r2: +        return 1 +    if r2 > r1: +        return -1 +    return 0 + +def dep_parenreduce(mysplit, mypos=0): +    """Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists: + +    >>> dep_parenreduce(['']) +    [''] +    >>> dep_parenreduce(['1', '2', '3']) +    ['1', '2', '3'] +    >>> dep_parenreduce(['1', '(', '2', '3', ')', '4']) +    ['1', ['2', '3'], '4'] +    """ + +    while mypos < len(mysplit): +        if mysplit[mypos] == "(": +            firstpos = mypos +            mypos = mypos + 1 +            while mypos < len(mysplit): +                if mysplit[mypos] == ")": +                    mysplit[firstpos:mypos+1] = [mysplit[firstpos+1:mypos]] +                    mypos = firstpos +                    break +                elif mysplit[mypos] == "(": +                    # recurse +                    mysplit = dep_parenreduce(mysplit,mypos) +                mypos = mypos + 1 +        mypos = mypos + 1 +    return mysplit + +def dep_opconvert(mysplit, myuse): +    "Does dependency operator conversion" + +    mypos   = 0 +    newsplit = [] +    while mypos < len(mysplit): +        if type(mysplit[mypos]) == types.ListType: +            newsplit.append(dep_opconvert(mysplit[mypos],myuse)) +            mypos += 1 +        elif mysplit[mypos] == ")": +            # mismatched paren, error +            return None +        elif mysplit[mypos]=="||": +            if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType): +                # || must be followed by paren'd list +                return None +            try: +                mynew = dep_opconvert(mysplit[mypos+1],myuse) +            except Exception, e: +                error("unable to satisfy OR dependancy: " + string.join(mysplit," || ")) +                raise e +            mynew[0:0] = ["||"] +            newsplit.append(mynew) +            mypos += 2 +        elif mysplit[mypos][-1] == "?": +            # use clause, i.e "gnome? ( foo bar )" +            # this is a quick and dirty hack so that repoman can enable all USE vars: +            if (len(myuse) == 1) and (myuse[0] == "*"): +                # enable it even if it's ! (for repoman) but kill it if it's +                # an arch variable that isn't for this arch. XXX Sparc64? +                if (mysplit[mypos][:-1] not in settings.usemask) or \ +                        (mysplit[mypos][:-1]==settings["ARCH"]): +                    enabled=1 +                else: +                    enabled=0 +            else: +                if mysplit[mypos][0] == "!": +                    myusevar = mysplit[mypos][1:-1] +                    enabled = not myusevar in myuse +                    #if myusevar in myuse: +                    #    enabled = 0 +                    #else: +                    #    enabled = 1 +                else: +                    myusevar=mysplit[mypos][:-1] +                    enabled = myusevar in myuse +                    #if myusevar in myuse: +                    #    enabled=1 +                    #else: +                    #    enabled=0 +            if (mypos +2 < len(mysplit)) and (mysplit[mypos+2] == ":"): +                # colon mode +                if enabled: +                    # choose the first option +                    if type(mysplit[mypos+1]) == types.ListType: +                        newsplit.append(dep_opconvert(mysplit[mypos+1],myuse)) +                    else: +                        newsplit.append(mysplit[mypos+1]) +                else: +                    # choose the alternate option +                    if type(mysplit[mypos+1]) == types.ListType: +                        newsplit.append(dep_opconvert(mysplit[mypos+3],myuse)) +                    else: +                        newsplit.append(mysplit[mypos+3]) +                mypos += 4 +            else: +                # normal use mode +                if enabled: +                    if type(mysplit[mypos+1]) == types.ListType: +                        newsplit.append(dep_opconvert(mysplit[mypos+1],myuse)) +                    else: +                        newsplit.append(mysplit[mypos+1]) +                # otherwise, continue +                mypos += 2 +        else: +            # normal item +            newsplit.append(mysplit[mypos]) +            mypos += 1 +    return newsplit + | 
