fune/python/mozbuild/mozpack/chrome/flags.py
Cristian Tuns c1b52fd95e Backed out 5 changesets (bug 1811850) for causing linting bustages(bugzilla) CLOSED TREE
Backed out changeset e8fcfc7f8108 (bug 1811850)
Backed out changeset f8950d716c9e (bug 1811850)
Backed out changeset f650123cc188 (bug 1811850)
Backed out changeset d96f90c2c58b (bug 1811850)
Backed out changeset c3b0f9666183 (bug 1811850)
2023-03-16 22:16:30 -04:00

277 lines
8.3 KiB
Python

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import re
from collections import OrderedDict
import six
from mozpack.errors import errors
from packaging.version import Version
class Flag(object):
"""
Class for flags in manifest entries in the form:
"flag" (same as "flag=true")
"flag=yes|true|1"
"flag=no|false|0"
"""
def __init__(self, name):
"""
Initialize a Flag with the given name.
"""
self.name = name
self.value = None
def add_definition(self, definition):
"""
Add a flag value definition. Replaces any previously set value.
"""
if definition == self.name:
self.value = True
return
assert definition.startswith(self.name)
if definition[len(self.name)] != "=":
return errors.fatal("Malformed flag: %s" % definition)
value = definition[len(self.name) + 1 :]
if value in ("yes", "true", "1", "no", "false", "0"):
self.value = value
else:
return errors.fatal("Unknown value in: %s" % definition)
def matches(self, value):
"""
Return whether the flag value matches the given value. The values
are canonicalized for comparison.
"""
if value in ("yes", "true", "1", True):
return self.value in ("yes", "true", "1", True)
if value in ("no", "false", "0", False):
return self.value in ("no", "false", "0", False, None)
raise RuntimeError("Invalid value: %s" % value)
def __str__(self):
"""
Serialize the flag value in the same form given to the last
add_definition() call.
"""
if self.value is None:
return ""
if self.value is True:
return self.name
return "%s=%s" % (self.name, self.value)
def __eq__(self, other):
return str(self) == other
class StringFlag(object):
"""
Class for string flags in manifest entries in the form:
"flag=string"
"flag!=string"
"""
def __init__(self, name):
"""
Initialize a StringFlag with the given name.
"""
self.name = name
self.values = []
def add_definition(self, definition):
"""
Add a string flag definition.
"""
assert definition.startswith(self.name)
value = definition[len(self.name) :]
if value.startswith("="):
self.values.append(("==", value[1:]))
elif value.startswith("!="):
self.values.append(("!=", value[2:]))
else:
return errors.fatal("Malformed flag: %s" % definition)
def matches(self, value):
"""
Return whether one of the string flag definitions matches the given
value.
For example,
flag = StringFlag('foo')
flag.add_definition('foo!=bar')
flag.matches('bar') returns False
flag.matches('qux') returns True
flag = StringFlag('foo')
flag.add_definition('foo=bar')
flag.add_definition('foo=baz')
flag.matches('bar') returns True
flag.matches('baz') returns True
flag.matches('qux') returns False
"""
if not self.values:
return True
for comparison, val in self.values:
if eval("value %s val" % comparison):
return True
return False
def __str__(self):
"""
Serialize the flag definitions in the same form given to each
add_definition() call.
"""
res = []
for comparison, val in self.values:
if comparison == "==":
res.append("%s=%s" % (self.name, val))
else:
res.append("%s!=%s" % (self.name, val))
return " ".join(res)
def __eq__(self, other):
return str(self) == other
class VersionFlag(object):
"""
Class for version flags in manifest entries in the form:
"flag=version"
"flag<=version"
"flag<version"
"flag>=version"
"flag>version"
"""
def __init__(self, name):
"""
Initialize a VersionFlag with the given name.
"""
self.name = name
self.values = []
def add_definition(self, definition):
"""
Add a version flag definition.
"""
assert definition.startswith(self.name)
value = definition[len(self.name) :]
if value.startswith("="):
self.values.append(("==", Version(value[1:])))
elif len(value) > 1 and value[0] in ["<", ">"]:
if value[1] == "=":
if len(value) < 3:
return errors.fatal("Malformed flag: %s" % definition)
self.values.append((value[0:2], Version(value[2:])))
else:
self.values.append((value[0], Version(value[1:])))
else:
return errors.fatal("Malformed flag: %s" % definition)
def matches(self, value):
"""
Return whether one of the version flag definitions matches the given
value.
For example,
flag = VersionFlag('foo')
flag.add_definition('foo>=1.0')
flag.matches('1.0') returns True
flag.matches('1.1') returns True
flag.matches('0.9') returns False
flag = VersionFlag('foo')
flag.add_definition('foo>=1.0')
flag.add_definition('foo<0.5')
flag.matches('0.4') returns True
flag.matches('1.0') returns True
flag.matches('0.6') returns False
"""
value = Version(value)
if not self.values:
return True
for comparison, val in self.values:
if eval("value %s val" % comparison):
return True
return False
def __str__(self):
"""
Serialize the flag definitions in the same form given to each
add_definition() call.
"""
res = []
for comparison, val in self.values:
if comparison == "==":
res.append("%s=%s" % (self.name, val))
else:
res.append("%s%s%s" % (self.name, comparison, val))
return " ".join(res)
def __eq__(self, other):
return str(self) == other
class Flags(OrderedDict):
"""
Class to handle a set of flags definitions given on a single manifest
entry.
"""
FLAGS = {
"application": StringFlag,
"appversion": VersionFlag,
"platformversion": VersionFlag,
"contentaccessible": Flag,
"os": StringFlag,
"osversion": VersionFlag,
"abi": StringFlag,
"platform": Flag,
"xpcnativewrappers": Flag,
"tablet": Flag,
"process": StringFlag,
"backgroundtask": StringFlag,
}
RE = re.compile(r"([!<>=]+)")
def __init__(self, *flags):
"""
Initialize a set of flags given in string form.
flags = Flags('contentaccessible=yes', 'appversion>=3.5')
"""
OrderedDict.__init__(self)
for f in flags:
name = self.RE.split(f)
name = name[0]
if name not in self.FLAGS:
errors.fatal("Unknown flag: %s" % name)
continue
if name not in self:
self[name] = self.FLAGS[name](name)
self[name].add_definition(f)
def __str__(self):
"""
Serialize the set of flags.
"""
return " ".join(str(self[k]) for k in self)
def match(self, **filter):
"""
Return whether the set of flags match the set of given filters.
flags = Flags('contentaccessible=yes', 'appversion>=3.5',
'application=foo')
flags.match(application='foo') returns True
flags.match(application='foo', appversion='3.5') returns True
flags.match(application='foo', appversion='3.0') returns False
"""
for name, value in six.iteritems(filter):
if name not in self:
continue
if not self[name].matches(value):
return False
return True