python/mozbuild/mozpack/path.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/python/mozbuild/mozpack/path.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,128 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +import posixpath
     1.9 +import os
    1.10 +import re
    1.11 +
    1.12 +'''
    1.13 +Like os.path, with a reduced set of functions, and with normalized path
    1.14 +separators (always use forward slashes).
    1.15 +Also contains a few additional utilities not found in os.path.
    1.16 +'''
    1.17 +
    1.18 +
    1.19 +def normsep(path):
    1.20 +    '''
    1.21 +    Normalize path separators, by using forward slashes instead of whatever
    1.22 +    os.sep is.
    1.23 +    '''
    1.24 +    if os.sep != '/':
    1.25 +        path = path.replace(os.sep, '/')
    1.26 +    return path
    1.27 +
    1.28 +
    1.29 +def relpath(path, start):
    1.30 +    rel = normsep(os.path.relpath(path, start))
    1.31 +    return '' if rel == '.' else rel
    1.32 +
    1.33 +
    1.34 +def abspath(path):
    1.35 +    return normsep(os.path.abspath(path))
    1.36 +
    1.37 +
    1.38 +def join(*paths):
    1.39 +    return normsep(os.path.join(*paths))
    1.40 +
    1.41 +
    1.42 +def normpath(path):
    1.43 +    return posixpath.normpath(normsep(path))
    1.44 +
    1.45 +
    1.46 +def dirname(path):
    1.47 +    return posixpath.dirname(normsep(path))
    1.48 +
    1.49 +
    1.50 +def commonprefix(paths):
    1.51 +    return posixpath.commonprefix([normsep(path) for path in paths])
    1.52 +
    1.53 +
    1.54 +def basename(path):
    1.55 +    return os.path.basename(path)
    1.56 +
    1.57 +
    1.58 +def splitext(path):
    1.59 +    return posixpath.splitext(normsep(path))
    1.60 +
    1.61 +
    1.62 +def split(path):
    1.63 +    '''
    1.64 +    Return the normalized path as a list of its components.
    1.65 +        split('foo/bar/baz') returns ['foo', 'bar', 'baz']
    1.66 +    '''
    1.67 +    return normsep(path).split('/')
    1.68 +
    1.69 +
    1.70 +def basedir(path, bases):
    1.71 +    '''
    1.72 +    Given a list of directories (bases), return which one contains the given
    1.73 +    path. If several matches are found, the deepest base directory is returned.
    1.74 +        basedir('foo/bar/baz', ['foo', 'baz', 'foo/bar']) returns 'foo/bar'
    1.75 +        ('foo' and 'foo/bar' both match, but 'foo/bar' is the deepest match)
    1.76 +    '''
    1.77 +    path = normsep(path)
    1.78 +    bases = [normsep(b) for b in bases]
    1.79 +    if path in bases:
    1.80 +        return path
    1.81 +    for b in sorted(bases, reverse=True):
    1.82 +        if b == '' or path.startswith(b + '/'):
    1.83 +            return b
    1.84 +
    1.85 +
    1.86 +re_cache = {}
    1.87 +
    1.88 +def match(path, pattern):
    1.89 +    '''
    1.90 +    Return whether the given path matches the given pattern.
    1.91 +    An asterisk can be used to match any string, including the null string, in
    1.92 +    one part of the path:
    1.93 +        'foo' matches '*', 'f*' or 'fo*o'
    1.94 +    However, an asterisk matching a subdirectory may not match the null string:
    1.95 +        'foo/bar' does *not* match 'foo/*/bar'
    1.96 +    If the pattern matches one of the ancestor directories of the path, the
    1.97 +    patch is considered matching:
    1.98 +        'foo/bar' matches 'foo'
    1.99 +    Two adjacent asterisks can be used to match files and zero or more
   1.100 +    directories and subdirectories.
   1.101 +        'foo/bar' matches 'foo/**/bar', or '**/bar'
   1.102 +    '''
   1.103 +    if not pattern:
   1.104 +        return True
   1.105 +    if not pattern in re_cache:
   1.106 +        pattern = re.escape(pattern)
   1.107 +        pattern = re.sub(r'(^|\\\/)\\\*\\\*\\\/', r'\1(?:.+/)?', pattern)
   1.108 +        pattern = re.sub(r'(^|\\\/)\\\*\\\*$', r'(?:\1.+)?', pattern)
   1.109 +        pattern = pattern.replace(r'\*', '[^/]*') + '(?:/.*)?$'
   1.110 +        re_cache[pattern] = re.compile(pattern)
   1.111 +    return re_cache[pattern].match(path) is not None
   1.112 +
   1.113 +
   1.114 +def rebase(oldbase, base, relativepath):
   1.115 +    '''
   1.116 +    Return relativepath relative to base instead of oldbase.
   1.117 +    '''
   1.118 +    if base == oldbase:
   1.119 +        return relativepath
   1.120 +    if len(base) < len(oldbase):
   1.121 +        assert basedir(oldbase, [base]) == base
   1.122 +        relbase = relpath(oldbase, base)
   1.123 +        result = join(relbase, relativepath)
   1.124 +    else:
   1.125 +        assert basedir(base, [oldbase]) == oldbase
   1.126 +        relbase = relpath(base, oldbase)
   1.127 +        result = relpath(relativepath, relbase)
   1.128 +    result = normpath(result)
   1.129 +    if relativepath.endswith('/') and not result.endswith('/'):
   1.130 +        result += '/'
   1.131 +    return result

mercurial