python/mozbuild/mozpack/chrome/flags.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/python/mozbuild/mozpack/chrome/flags.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,255 @@
     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 re
     1.9 +from distutils.version import LooseVersion
    1.10 +from mozpack.errors import errors
    1.11 +from collections import OrderedDict
    1.12 +
    1.13 +
    1.14 +class Flag(object):
    1.15 +    '''
    1.16 +    Class for flags in manifest entries in the form:
    1.17 +        "flag"   (same as "flag=true")
    1.18 +        "flag=yes|true|1"
    1.19 +        "flag=no|false|0"
    1.20 +    '''
    1.21 +    def __init__(self, name):
    1.22 +        '''
    1.23 +        Initialize a Flag with the given name.
    1.24 +        '''
    1.25 +        self.name = name
    1.26 +        self.value = None
    1.27 +
    1.28 +    def add_definition(self, definition):
    1.29 +        '''
    1.30 +        Add a flag value definition. Replaces any previously set value.
    1.31 +        '''
    1.32 +        if definition == self.name:
    1.33 +            self.value = True
    1.34 +            return
    1.35 +        assert(definition.startswith(self.name))
    1.36 +        if definition[len(self.name)] != '=':
    1.37 +            return errors.fatal('Malformed flag: %s' % definition)
    1.38 +        value = definition[len(self.name) + 1:]
    1.39 +        if value in ('yes', 'true', '1', 'no', 'false', '0'):
    1.40 +            self.value = value
    1.41 +        else:
    1.42 +            return errors.fatal('Unknown value in: %s' % definition)
    1.43 +
    1.44 +    def matches(self, value):
    1.45 +        '''
    1.46 +        Return whether the flag value matches the given value. The values
    1.47 +        are canonicalized for comparison.
    1.48 +        '''
    1.49 +        if value in ('yes', 'true', '1', True):
    1.50 +            return self.value in ('yes', 'true', '1', True)
    1.51 +        if value in ('no', 'false', '0', False):
    1.52 +            return self.value in ('no', 'false', '0', False, None)
    1.53 +        raise RuntimeError('Invalid value: %s' % value)
    1.54 +
    1.55 +    def __str__(self):
    1.56 +        '''
    1.57 +        Serialize the flag value in the same form given to the last
    1.58 +        add_definition() call.
    1.59 +        '''
    1.60 +        if self.value is None:
    1.61 +            return ''
    1.62 +        if self.value is True:
    1.63 +            return self.name
    1.64 +        return '%s=%s' % (self.name, self.value)
    1.65 +
    1.66 +
    1.67 +class StringFlag(object):
    1.68 +    '''
    1.69 +    Class for string flags in manifest entries in the form:
    1.70 +        "flag=string"
    1.71 +        "flag!=string"
    1.72 +    '''
    1.73 +    def __init__(self, name):
    1.74 +        '''
    1.75 +        Initialize a StringFlag with the given name.
    1.76 +        '''
    1.77 +        self.name = name
    1.78 +        self.values = []
    1.79 +
    1.80 +    def add_definition(self, definition):
    1.81 +        '''
    1.82 +        Add a string flag definition.
    1.83 +        '''
    1.84 +        assert(definition.startswith(self.name))
    1.85 +        value = definition[len(self.name):]
    1.86 +        if value.startswith('='):
    1.87 +            self.values.append(('==', value[1:]))
    1.88 +        elif value.startswith('!='):
    1.89 +            self.values.append(('!=', value[2:]))
    1.90 +        else:
    1.91 +            return errors.fatal('Malformed flag: %s' % definition)
    1.92 +
    1.93 +    def matches(self, value):
    1.94 +        '''
    1.95 +        Return whether one of the string flag definitions matches the given
    1.96 +        value.
    1.97 +        For example,
    1.98 +            flag = StringFlag('foo')
    1.99 +            flag.add_definition('foo!=bar')
   1.100 +            flag.matches('bar') returns False
   1.101 +            flag.matches('qux') returns True
   1.102 +            flag = StringFlag('foo')
   1.103 +            flag.add_definition('foo=bar')
   1.104 +            flag.add_definition('foo=baz')
   1.105 +            flag.matches('bar') returns True
   1.106 +            flag.matches('baz') returns True
   1.107 +            flag.matches('qux') returns False
   1.108 +        '''
   1.109 +        if not self.values:
   1.110 +            return True
   1.111 +        for comparison, val in self.values:
   1.112 +            if eval('value %s val' % comparison):
   1.113 +                return True
   1.114 +        return False
   1.115 +
   1.116 +    def __str__(self):
   1.117 +        '''
   1.118 +        Serialize the flag definitions in the same form given to each
   1.119 +        add_definition() call.
   1.120 +        '''
   1.121 +        res = []
   1.122 +        for comparison, val in self.values:
   1.123 +            if comparison == '==':
   1.124 +                res.append('%s=%s' % (self.name, val))
   1.125 +            else:
   1.126 +                res.append('%s!=%s' % (self.name, val))
   1.127 +        return ' '.join(res)
   1.128 +
   1.129 +
   1.130 +class VersionFlag(object):
   1.131 +    '''
   1.132 +    Class for version flags in manifest entries in the form:
   1.133 +        "flag=version"
   1.134 +        "flag<=version"
   1.135 +        "flag<version"
   1.136 +        "flag>=version"
   1.137 +        "flag>version"
   1.138 +    '''
   1.139 +    def __init__(self, name):
   1.140 +        '''
   1.141 +        Initialize a VersionFlag with the given name.
   1.142 +        '''
   1.143 +        self.name = name
   1.144 +        self.values = []
   1.145 +
   1.146 +    def add_definition(self, definition):
   1.147 +        '''
   1.148 +        Add a version flag definition.
   1.149 +        '''
   1.150 +        assert(definition.startswith(self.name))
   1.151 +        value = definition[len(self.name):]
   1.152 +        if value.startswith('='):
   1.153 +            self.values.append(('==', LooseVersion(value[1:])))
   1.154 +        elif len(value) > 1 and value[0] in ['<', '>']:
   1.155 +            if value[1] == '=':
   1.156 +                if len(value) < 3:
   1.157 +                    return errors.fatal('Malformed flag: %s' % definition)
   1.158 +                self.values.append((value[0:2], LooseVersion(value[2:])))
   1.159 +            else:
   1.160 +                self.values.append((value[0], LooseVersion(value[1:])))
   1.161 +        else:
   1.162 +            return errors.fatal('Malformed flag: %s' % definition)
   1.163 +
   1.164 +    def matches(self, value):
   1.165 +        '''
   1.166 +        Return whether one of the version flag definitions matches the given
   1.167 +        value.
   1.168 +        For example,
   1.169 +            flag = VersionFlag('foo')
   1.170 +            flag.add_definition('foo>=1.0')
   1.171 +            flag.matches('1.0') returns True
   1.172 +            flag.matches('1.1') returns True
   1.173 +            flag.matches('0.9') returns False
   1.174 +            flag = VersionFlag('foo')
   1.175 +            flag.add_definition('foo>=1.0')
   1.176 +            flag.add_definition('foo<0.5')
   1.177 +            flag.matches('0.4') returns True
   1.178 +            flag.matches('1.0') returns True
   1.179 +            flag.matches('0.6') returns False
   1.180 +        '''
   1.181 +        value = LooseVersion(value)
   1.182 +        if not self.values:
   1.183 +            return True
   1.184 +        for comparison, val in self.values:
   1.185 +            if eval('value %s val' % comparison):
   1.186 +                return True
   1.187 +        return False
   1.188 +
   1.189 +    def __str__(self):
   1.190 +        '''
   1.191 +        Serialize the flag definitions in the same form given to each
   1.192 +        add_definition() call.
   1.193 +        '''
   1.194 +        res = []
   1.195 +        for comparison, val in self.values:
   1.196 +            if comparison == '==':
   1.197 +                res.append('%s=%s' % (self.name, val))
   1.198 +            else:
   1.199 +                res.append('%s%s%s' % (self.name, comparison, val))
   1.200 +        return ' '.join(res)
   1.201 +
   1.202 +
   1.203 +class Flags(OrderedDict):
   1.204 +    '''
   1.205 +    Class to handle a set of flags definitions given on a single manifest
   1.206 +    entry.
   1.207 +    '''
   1.208 +    FLAGS = {
   1.209 +        'application': StringFlag,
   1.210 +        'appversion': VersionFlag,
   1.211 +        'platformversion': VersionFlag,
   1.212 +        'contentaccessible': Flag,
   1.213 +        'os': StringFlag,
   1.214 +        'osversion': VersionFlag,
   1.215 +        'abi': StringFlag,
   1.216 +        'platform': Flag,
   1.217 +        'xpcnativewrappers': Flag,
   1.218 +        'tablet': Flag,
   1.219 +    }
   1.220 +    RE = re.compile(r'([!<>=]+)')
   1.221 +
   1.222 +    def __init__(self, *flags):
   1.223 +        '''
   1.224 +        Initialize a set of flags given in string form.
   1.225 +           flags = Flags('contentaccessible=yes', 'appversion>=3.5')
   1.226 +        '''
   1.227 +        OrderedDict.__init__(self)
   1.228 +        for f in flags:
   1.229 +            name = self.RE.split(f)
   1.230 +            name = name[0]
   1.231 +            if not name in self.FLAGS:
   1.232 +                errors.fatal('Unknown flag: %s' % name)
   1.233 +                continue
   1.234 +            if not name in self:
   1.235 +                self[name] = self.FLAGS[name](name)
   1.236 +            self[name].add_definition(f)
   1.237 +
   1.238 +    def __str__(self):
   1.239 +        '''
   1.240 +        Serialize the set of flags.
   1.241 +        '''
   1.242 +        return ' '.join(str(self[k]) for k in self)
   1.243 +
   1.244 +    def match(self, **filter):
   1.245 +        '''
   1.246 +        Return whether the set of flags match the set of given filters.
   1.247 +            flags = Flags('contentaccessible=yes', 'appversion>=3.5',
   1.248 +                          'application=foo')
   1.249 +            flags.match(application='foo') returns True
   1.250 +            flags.match(application='foo', appversion='3.5') returns True
   1.251 +            flags.match(application='foo', appversion='3.0') returns False
   1.252 +        '''
   1.253 +        for name, value in filter.iteritems():
   1.254 +            if not name in self:
   1.255 +                continue
   1.256 +            if not self[name].matches(value):
   1.257 +                return False
   1.258 +        return True

mercurial