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